一、 字符串与字符数组
c语言没有字符串类型,用字符数组存放字符串。
字符指针有以下几种:
第一种:
char a='c';
char* p=&a;//将a的地址赋给字符指针p
*p='d';//修改字符指针p指向内存空间的内容
printf("%c",*p);//输出:d
C语言中数组名代表的是数组首元素的地址,除了一下两种情况:
- 在函数sizeof(数组名)中,计算的是整个数组的大小。
int a[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(a)/sizeof(int));
这里输出10,sizeof(a)表示整个数组所占的字节。
- &数组名,这里取出的是整个数组的地址。
int a[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(&a)/sizeof(int));
这里输出也是10,sizeof(&a数)表示整个数组所占的字节。
第二种:
char arr[] = "abcdef"; // arr数组的长度为7,不是6,包含终止符'\0'
char *p = arr; // 数组名就是数组首元素的地址 数组名在表达式中被解读为指向数组首元素的指针, 即数组名在表达式中值为数组首元素的地址。
printf("%c\n", *p); // 解引用后得到字符a 输出:a,相当于printf("%c\n", arr);
printf("%s\n", p); // 输出字符指针p所指向的字符串 输出:abcdef,相当于printf("%s\n", &arr); &arr表示整个数组
printf("%s\n", arr); // 数组名就是首元素的地址,故与p相同:输出:abcdef
第三种:
char *p = "abcdef"; // 该字符串是一个常量字符串,且把该字符串的首字符的地址放到字符指针p中
// 该字符串在内存中的表示是:a b c d e f \0,把该字符串的地址存到字符变量p中,该地址为字符串首字符的地址。
printf("%c\n", *p); // p指向的是字符串首字符的地址,即'a'的地址,则解引用后,得到字符'a'; 输出:a
printf("%s\n", p); // 把字符指针p所指向的字符串给打印出来, 输出:abcdef
二、常量字符串
- 常量字符串的内容是不能被修改的。
- 内存中只存在一份,多个字符指针指向相同的字符串,这些指针中存放的地址相同。
- 将其赋值给字符指针时,是把其首字符的地址赋值给字符指针。
三、指针数组
指针数组时存放指针的数组,数组元素时地址。
int arr1[5] = {0}; // 整型数组,因为数组的元素是整型
char arr2[5] = {0}; // 字符数组,因为数组的元素是字符类型
int* arr3[3]; // 存放整型指针的数组----指针数组
char* arr4[4]; // 存放字符指针的数组----指针数组
应用:
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 6,7,8,9,10};
int arr3[] = { 11,12,13,14,15 };
int* parr[3] = { arr1, arr2, arr3 };
int i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
// 以下三种写法都对:都可以一次打印每个数组的元素
// 找到数组的每个元素:parr[i],其中每一个元素是一个数组(arr1..3)
// +j表示找到指定数组中第j个元素的地址,在对其进行解引用,则找到对应的值
//printf("%d\t", *(parr[i] + j)); // 得到第i行的地址后向后偏移j的地址,再进行解引用
//printf("%d\t", parr[i][j]);
printf("%d\t", *(*(parr+i)+j));
}
printf("\n");
}
return 0;
}
四、数组指针
数组指针是一个指针,指向的是一个数组。
数组指针 – 指向数组的指针 — 可以存放数组的地址
int arr[10] = {0}; // 定义并初始化一个含有10个元素的整型数组,数组名arr:是首元素地址(等同于&arr[0] <—> arr)。
&arr: 是数组的地址(也是数组首元素的地址),但是&arr 和 arr 和&arr[0]的地址相同,只是&arr + 1是跨过整个数组长度,另外两个地址+1是跨过数组中一个元素。
现在我们把&arr存起来,放到一个指针变量中,该指针变量就是数组指针。
即为:int(*p)[10] = &arr;
指针数组与数组指针的区分:
int* p1[10]; // 指针数组
int (*p2)[10]; // 数组指针
指向char指针类型的数组的指针:
char* arr[5];
char* (*p)[5] = &arr;// 因为存放的是数组的地址,因此需要用一个数组指针来接收该地址:(*p)[], 由于数组的元素个数为5且类型为char*,因此写作:char* (*p)[5] = &arr;
// 剖析:
// (*p):代表p是一个指针,p是指针变量的名字
// [5]:代表p指向的数组是5个元素。
// char*:代表指向的数组的元素类型是char*
数组的地址和数组名的比较:
int main()
{
int arr[5] = { 1,2,3,4,5 };
int(*p)[5] = &arr;
printf("arr = %p\n", arr); //数组名表示数组首元素的地址
printf("&arr = %p\n", &arr); // 数组的地址
printf("p = %p\n", p); // 存放数组地址的指针变量
printf("arr + 1 = %p\n", arr + 1); // 指向数组的下一个元素的地址
printf("&arr + 1 = %p\n", &arr + 1); // 指向下一个数组的地址,即数组最后一个元素后面的地址
printf("p + 1 = %p\n", p + 1); // 同上
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", (*p)[i]);
printf("%d ", *(*p + i)); // *p是解引用得到arr,再加i表示得到第i个元素的地址,再解引用
//printf("%d ", arr[i]); //结果同上,依次打印数组中每个元素
}
return 0;
}
数组指针主要用在二维及以上维度的数组
int arr[2][3] = {{1,2,3}, {4,5,6}};
对于二维数组arr,数组名(arr)也是首元素的地址,把二维数组看成一维数组,即每一行看做一个元素,即第一行是二维数组的第一个元素(首元素);
简而言之,对于二维数组,数组名是首元素的地址;首元素是第一行;第一行是一个二维数组。
使用:当把二维数组名作为实参进行传递时,且形参写成指针的形式,则需要一个指针来接收,因为数组名是首元素(第一行、一维数组)的地址,而数组的地址应该放到数组指针中。只不过这个数组指针指向的不是二维数组,而是指向第一行(一维数组的,其有3个元素,每个元素为int),故形参应该写成:int (*p)[3];
因为p指向的是一个数组,所以p+1是跳过一个数组的长度,即指向了第二行。
数组指针作为函数参数:
// 参数是数组的形式
void print1(int arr[2][3], int r, int c)
{
int i, j;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
// 参数是指针的形式
void print2(int (*p)[3], int r, int c)
{
int i, j;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
// p + i:跳过i行,即指向了第i行
// *(p + i):找到了第i行,即拿到了这一行的数组名
// *(p + i) + j:指向了i行这个一维数组中第j个元素,对其进行解引用,则找到了第i行第j列的元素
printf("%d ", *(*(p + i) + j)); // 同上
printf("%d ", (*(p + i))[j]); // 这种方式也对前面找到一维数组的数组名
printf("%d ", p[i][j]); // 同上
printf("%d ", *(p[i] + j)); // 同上
}
printf("\n");
}
}
int main()
{
int arr[2][3] = { 1,2,3,4,5,6 };
print1(arr, 2, 3); // 形参为数组形式
print2(arr, 2, 3); // 形参为数组指针形式
return 0;
}
指针和数组的区分:
int arr1[5]; // arr1是一个包含5个元素的整型数组
int* arr2[5]; // arr2是一个包含5个元素的int*数组,即指针数组
int *(*p)[5]; // p是一个指向包含5个int类型数组的指针, 即数组指针
int (*p1[10])[5]; // p1是一个数组,该数组有10个元素,每个元素的类型为:数组指针,该数组指针指向的元素有5个元素,每个元素是int类型。把数组名和方块去掉,剩下的就是元素的类型。
数组和指针作为函数参数
一维数组作为函数参数,可以用数组和指针两种形式来接受参数。
void fun1(int arr[]) { ; } // 用数组进行接收,不指定数组大小
void fun2(int arr[5]) { ; } // 用数组进行接收,指定数组大小,指定也不会用
void fun3(int *parr) { ; } // 用指针进行接收,因为数组元素为int,所以指针类型为int
void pfun1(int *arr[]) { ; }
void pfun2(int *arr[5]) { ; } // 同上,数组大小可以指定和不指定
void pfun3(int **parr) { ; } // 数组元素为指针,故用二级指针来接收
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int* arr2[5];
fun1(arr1);
fun2(arr1);
fun3(arr1);
pfun1(arr2);
pfun2(arr2);
pfun3(arr2);
return 0;
}
二维数组作为函数参数
可以用数组和指针两种形式来接受参数
void fun1(int arr[2][3]) { ; }
void fun2(int arr[][3]) { ; } // 行数可以省略
//void fun3(int arr[][]) {;} // 列数不可以省略, error
//void fun4(int arr[2][]) {;} // error
void pfun1(int(*p)[3]) { ; } // 用指针来接收
// 因为arr是数组名,就是首元素的地址,即第一行的地址
// 即一个一维数组的地址,即数组的地址,因此要用数组指针来接收
int main()
{
int arr[2][3] = { 1,2,3,4,5,6 };
fun1(arr);
fun2(arr);
//fun3(arr);
//fun4(arr);
pfun1(arr);
return 0;
}
一级指针作为函数参数
当参数为一级指针时,如果指向的是一个数组,可以用指针来接受,也可以用数组形式来接受
void fun1(int *parr, int len)
{
int i;
for (i = 0; i < len; i++)
{
printf("%d ", *(parr + i)); // 1 2 3 4 5
printf("%d ", parr[i]); // 1 2 3 4 5
}
}
void fun2(int parr[], int len)
{
int i;
for (i = 0; i < len; i++)
{
printf("%d ", parr[i]); // 1 2 3 4 5
printf("%d ", *(parr + i)); // 1 2 3 4 5
}
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
int* p = arr;
int len = sizeof(arr) / sizeof(arr[0]);
fun1(p, len);
fun2(p, len);
return 0;
}
二级指针作为函数参数
形参要写成二级指针的形式,如void fun1(int **p) {;}
实参要用指针变量的地址来进行传递,也可以用指针数组的数组名来进行传递。