C语言字符数组、指针总结

一、 字符串与字符数组

c语言没有字符串类型,用字符数组存放字符串。
字符指针有以下几种:
第一种:

char a='c';
char* p=&a;//将a的地址赋给字符指针p
*p='d';//修改字符指针p指向内存空间的内容
printf("%c",*p);//输出:d

C语言中数组名代表的是数组首元素的地址,除了一下两种情况:

  1. 在函数sizeof(数组名)中,计算的是整个数组的大小。
int a[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(a)/sizeof(int));

这里输出10,sizeof(a)表示整个数组所占的字节。

  1. &数组名,这里取出的是整个数组的地址。
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
二、常量字符串
  1. 常量字符串的内容是不能被修改的。
  2. 内存中只存在一份,多个字符指针指向相同的字符串,这些指针中存放的地址相同。
  3. 将其赋值给字符指针时,是把其首字符的地址赋值给字符指针。
三、指针数组

指针数组时存放指针的数组,数组元素时地址。

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) {;}
实参要用指针变量的地址来进行传递,也可以用指针数组的数组名来进行传递。

你可能感兴趣的:(C语言基础,c语言,c++,算法,数据结构)