C语言回顾深入学习:数组与指针
C数组
数组是用来存储一系列相同类型的变量的顺序集合。所有的数组都是由连续的内存位置组成。
声明数组
需要指定元素的类型和元素的数量
type arrayName [ arraySize ];
初始化数组
//大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//省略掉了数组的大小,数组的大小则为初始化时元素的个数。
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//为数组中某个元素赋值
balance[4] = 50.0;
数组元素可以通过数组名称加索引进行访问。
多维数组
多维数组声明的一般形式如下:
type name[size1][size2]...[sizeN];
二维数组,在本质上,是一个一维数组的列表,其声明及初始化:
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
//内部嵌套的括号是可选的,下面的初始化与上面是等同的
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
传递数组给函数
要在函数中传递一个数组作为参数,必须以下面三种方式来声明函数形式参数。这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。
1. 形式参数是一个指针:
void myFunction(int *param)
{
body of function
}
2. 形式参数是一个已定义大小的数组:
void myFunction(int param[])
{
body of function
}
3. 形式参数是一个未定义大小的数组:
void myFunction(int *param)
{
body of function
}
从函数返回数组
要从函数返回一个一维数组,必须声明一个返回指针的函数,:
int * myFunction()
{
body of function
}
C 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
示例:
/*
从函数返回一个由10个随机数构成的数组
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
static int r[10];
int i;
/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 10; ++i)
{
r[i] = rand();
printf( "r[%d] = %d\n", i, r[i]);
}
return r;
}
/* 要调用上面定义函数的主函数 */
int main ()
{
/* 一个指向整数的指针 */
int *p;
int i;
p = getRandom();
for ( i = 0; i < 10; i++ )
{
printf( "*(p + %d) : %d\n", i, *(p + i));
}
return 0;
}
指向数组的指针
数组名是一个指向数组中第一个元素的常量指针。
double *p;
double balance[10];
p = balance;
balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。 把 p 赋值为 balance 的第一个元素的地址。使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。
注意:
1. *p+4 是指 p 是指向balance数组的首地址(&balance[0])的指针(&表示取地址),*p是对首地址的元素取值(*在赋值符号右边和表达式中表示取值),该值加4即为 balance[0] +4
2. *(p+4) 是指 p 是指向balance数组的首地址(&balance[0])的指针(&表示取地址) , p+4表示地址加上4,*再对p+4取值,所以,*(p+4) 表示 balance[4] (第5个元素的值)。
C枚举
枚举是 C 语言中的一种基本数据类型。枚举语法定义格式为:
enum enumName {enum1,enum2,……};
可以在定义枚举类型时改变枚举元素的值,没有指定值的枚举元素,其值为前一元素加 1。第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
枚举变量的定义
有三种方式来定义枚举变量
1. 先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
2. 定义枚举类型的同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;
3, 省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。不过在一些特殊的情况下,枚举类型连续的值连续时是可以实现有条件的遍历。
示例(使用 for 来遍历枚举的元素):
#include<stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
// 遍历枚举元素
for (day = MON; day <= SUN; day++) {
printf("枚举元素:%d \n", day);
}
}
枚举类型不连续,这种枚举无法遍历,示例:
enum
{
ENUM_0,
ENUM_10 = 10,
ENUM_11
};
C 指针
学习 C 语言的指针,是很有必要的。它不但可以简化任务,而且比如进行动态内存分配,没有指针是无法执行的。变量都有内存位置,变量都定义了可以使用连字号(&)运算符访问的地址。
什么是指针?
指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。所以,指针的值是它所指向变量的地址。
指针变量声明的一般形式为:
type *var-name;
所有指针的值的类型都是一样的,都是一个代表内存地址的十六进制数。指针所指向的变量或常量的数据类型不同。
如何使用指针?
1. 定义一个指针变量
2. 把变量地址赋值给指针
3. 访问指针变量中可用地址的值。
这些是通过使用一元运算符 *来返回位于操作数所指地址的变量的值。示例如下:
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
/*取变量地址使用& */
printf("Address of var variable: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %p\n", ip );
/* 使用指针访问值(*是取对应内存地址的值) */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
C 中的 NULL 指针
声明指针变量的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针,空指针指向的地址是0x0。
空指针是一个定义在标准库中的值为零的常量。在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。
然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针是空值(零值(Null)),则假定它不指向任何东西。
指针的算术运算
C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。
ptr++
假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,ptr 每增加一次,它都将指向下一个整数位置,如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
递增或递减变量指针
访问数组中的每一元素:
/*指针递增遍历*/
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针中的数组地址 */
ptr = var;
for ( i = 0; i < MAX; i++)
{
printf("存储地址:var[%d] = %x\n", i, ptr );
printf("存储值:var[%d] = %d\n", i, *ptr );
/* 移动到下一个位置 */
ptr++;
}
return 0;
}
/*指针递减遍历*/
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针中最后一个元素的地址 */
ptr = &var[MAX-1];
for ( i = MAX; i > 0; i--)
{
printf("存储地址:var[%d] = %x\n", i-1, ptr );
printf("存储值:var[%d] = %d\n", i-1, *ptr );
/* 移动到下一个位置 */
ptr--;
}
return 0;
}
指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。指针直接比较是地址大小比较。需要比较指针所指向变量的值可以使用*号取值之后再进行比较。修改上述递增访问数组元素的代码,变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],就把变量指针进行递增,如下:
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针中第一个元素的地址 */
ptr = var;
i = 0;
while ( ptr <= &var[MAX - 1] )
{
printf("Address of var[%d] = %x\n", i, ptr );
printf("Value of var[%d] = %d\n", i, *ptr );
/* 指向上一个位置 */
ptr++;
i++;
}
return 0;
}
指针数组
指针数组的声明:
type *pointerArrayName[size];
//例如,声明一个由10个指向整数的指针构成的指针数组:
/*int *ptr[10];*/
下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr[MAX];
for ( i = 0; i < MAX; i++)
{
ptr[i] = &var[i]; /* 赋值为整数的地址 */
}
for ( i = 0; i < MAX; i++)
{
//ptr[i]是指针数组中的指针,是整数的地址;*对指针指向整数的地址取出值
printf("Value of var[%d] = %d\n", i, *ptr[i] );
}
return 0;
}
也可以用一个指向字符的指针数组来存储一个字符串列表,如下:(有点疑问的,后续研究)
#include <stdio.h>
const int MAX = 4;
int main ()
{
const char *names[] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};
int i = 0;
for ( i = 0; i < MAX; i++)
{
//names[i]是指针数值*names[]中的元素,是字符指针,names[i]所指向的变量的地址存放的值是字符串的首字母
printf("Value of names[%d] = %s\n", i, names[i] );
}
return 0;
}
指向指针的指针
一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号:
type **pointerName;
当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符:
#include <stdio.h>
int main ()
{
int var,*ptr, **pptr;
var = 3000;
/* 获取 var 的地址 */
ptr = &var;
/* 使用运算符 & 获取 指向变量的指针ptr 的地址 */
pptr = &ptr;
printf("Value of var = %d\n", var );
printf("Value available at *ptr = %d\n", *ptr );
/* 使用 **pptr 获取值 */
printf("Value available at **pptr = %d\n", **pptr);
return 0;
}
传递指针给函数
C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。
#include <stdio.h>
#include <time.h>
void getSeconds(unsigned long *par);
//也可以将数组名作为指针传递给函数,数组名就是数组的首地址。
int main ()
{
unsigned long sec;
getSeconds( &sec );//传递指针给函数,是传递的地址
/* 输出实际值 */
printf("Number of seconds: %ld\n", sec );
return 0;
}
void getSeconds(unsigned long *par)
{
/* 获取当前的秒数 */
*par = time( NULL );//par指针指向的sec地址存储的值赋值为当前时间秒数
return;
}
从函数返回指针
C 允许您从函数返回指针。为此,必须声明一个返回指针的函数,如下所示:
int * myFunction()
{
body of function
}
另外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为 static 变量。:
因为局部变量是存储在内存的栈区内,当函数调用结束后,局部变量所占的内存地址便被释放了,因此当其函数执行完毕后,函数内的变量便不再拥有那个内存地址,所以不能返回其指针。
除非将其变量定义为 static 变量,static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。