关于c数组、字符数组、指针的一些问题

6 篇文章 0 订阅

c语言:数组、字符串与指针

相关内容很多,不断整理。
通过这一部分加深对内存的认知。

指针

常用的就是一维和二维指针,指针有两个属性:存放的值(别人的地址)和自身地址
一维指针指向常量的地址,二维地址指向一维指针的地址。
当指针和数组组合起来的时候,要知道
1、指针存放的是谁的地址;
2、数组存放的数据类型是什么;

指针变量+1,就是指针指向从这个元素的内存到下一个内存,走过的距离看指向元素的大小:比如sizeof(int);

数组

基本概念:数组是多个相同类型数据的集合,并且在内存中分布在连续的地址单元中
数组名字就是数组的首地址,然后利用“内存连续”“数据类型相同”,通过指针++的方式遍历数组的每个数据:

一维数组

	int arr1[5] = { 1,2,3,4,5 };
	for (int i = 0; i < 5; i++) {
		printf("%d:%p\t", i, arr+i);
		//地址不断往前+4byte(sizeof(int)=4byte) 
	}
	char ch1[5] = { "abcde" };
	for (int i = 0; i < 5; i++) {
		printf("%d:%p\t", i, ch1+i);
		//地址不断往前+1byte(sizeof(char)=1byte) 
	}

二维数组

二维数组实际在内存中也是连续的n*m个数据,只是用了一个二维指针,
请看方式二:想象成一个二维表格,二维指针指行,一维指针指列,也就是*(a+i)确定为1+i行(起始为0),*(*(a+i)+j)在这一行上找1+j列。
方式三:告诉我们二维数组实际在内存中也是连续的n*m个数据,通过*p=a,找到了数组a的首地址,然后通过++方式遍历了n*m个数据,因为是int型,即每个数组元素占据4byte大小的内存,p每次+1,就是地址+4byte,由于内存连续,所以:p=a[0][0],p+1=a[0][1]....p+8=a[3][3]

	//两种定义方式
	int a[3][3] = { { 1,2,3 },{4,5,6},{7,8,9} };
	int b[][3] = { {1,2,3} ,{4,5,6} };
	int* p = a;//这里的a先相当于 &a[0][0]  &a[0]   而不是&a,&a是整个数组的地址,int*pp=&a  
	printf("sizeof(**a)=%d\n", sizeof(**a));//4     ==>sizeof(a[0][0])	(*a+1)==>+4
	printf("sizeof(*a)=%d\n", sizeof(*a));//4*3=12	==>sizeof(int* a[3])  a+1==>+12
	printf("sizeof(a)=%d\n", sizeof(a));//4*3*3=36 这里的a就是整个数组	&a+1==>+36
	printf("sizeof(b)=%d\n", sizeof(b));//4*3*2=24

#if 1  //三种指针遍历方式
//方式一:
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			printf("%d\t", a[i][j]);
		}
		printf("\n");
	}
//方式二:
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			printf("%d\t", *(*(a+i)+j));
		}
		printf("\n");
	}
//方式三:
		for (int i = 0; i < sizeof(a) / sizeof(int); i++) {
		printf("%d\t", *p + i);
	}
	printf("\n");

数组+指针:int (*a)[5],int *a[5]

先给结论:
int (*a)[5] //定义一个int型指针a,a指向一个大小为 5sizeof(int) 的内存;
int *a[5] 定义一个数组,类型为int
,数组名为a ,a也是首地址,大小为5sizeof(int)

这里容易被迷惑,搞不清楚情况,这里到底是个啥;首先要知道符号优先级:[ ] > *
所以int *a[5] ==> (int*) (a[5]) 也就是开辟一块内存里面放一个大小为5的数组,这个数组存放数据类型为int*,这个数组大小也就是 sizeof(int*)*5=20byte;(指针大小都是4byte)
int (*a)[10] 则是定义一个int型指针 a,指向一个大小为10的数组,数组数据为int型。
要注意 前面int *a[10] 是数组,后者不是,只是一个指针

	int* b[] = {1,2,3,4,5}; 
	printf("sizeof(b)=%d\n", sizeof(b));	//20
	printf("b=%p\n",b);						//008FF88C  指向 1 的指针的地址
	//相当于 int* p=&(1); c2[0]=p;  这里&(1)是指1的地址   
	//当然 这里的1 2  等看作常量,存放存于rodata段,没有对应的变量。
	printf("b+1=%p\n", b+1);				//008FF890 =008FF88C+4
	printf("*b=%p\n", *b);					//1
	printf("*(b+1)=%p\n", *(b + 1));//2
	char* c2[] = {'a','b','c','d' };
	printf("sizeof(c2)=%d\n", sizeof(c2));	//16
	printf("c2=%p\n", c2);					//008FF8E8  指向'a'的指针的地址
	//相当于 char* p=&('a'); c2[0]=p; 
	printf("c2+1=%p\n", c2 + 1);			//008FF8EC =008FF8E8+4
	printf("*c2=%c\n", *c2);				//a
	printf("*(c2+1)=%c\n", *(c2 + 1));		//b
	int arr2[5] = { 1,2,3,4,5 };
	int arr3[7] = { 1,2,3,4,5,6,7 };
	int(*c)[5] = arr2;//12345   
	printf("%p %p %p \n", &arr2[0], &arr2[1], &arr2[2]);
	printf("%p %p %p \n", c, c + 1, c + 2);  
	//007AF614 007AF628 007AF63C  说明:c指向的是一个大小为5的int数组,所以每次+1 移动5*4==20byte
	printf("%p %p %p \n", *c, *c+1,*c+2);				
	//*c==&arr2[0] *c+1==&arr2[1]   即*c指向的是int* 
	printf("%d %d %d \n", **c, *(*c + 1), *(*c + 2));//1 2 3
	c = arr3;//123
	printf("%p %p %p \n",&arr3[0], &arr3[1], &arr3[2]); //00A0FD24 00A0FD28 00A0FD2C
	printf("%p %p %p \n", c,c+2);						//00A0FD24 00A0FD4C 00A0FE64
	printf("%p %p %p \n", *c, *c + 1, *c + 6);			//00A0FD24 00A0FD28 00A0FD3C
	printf("%d %d %d \n", **c, *(*c + 1), *(*c + 6)); //1 2 7
	//虽然在监视里面显示c为int[5];但是实际上可以通过*(*c + i)访问后面的内存,这里c=arr3,可以通过*(*c + 6)访问到arr3[6](因为数组的内存连续!)
	for (int i = 0; i < 20; i++) {
		printf("%d:%p\t", i, *c + i);
		//地址不断往前+4byte(int) *c->int
	}
	char c1[] = { "abcdefg" };
	char(*c3)[5]=c1;
	for (int i = 0; i < 20; i++) {
		printf("%d:%p\t", i, *c3 + i);
		//地址不断往前+1byte (char)   *c->char
	}

测试题:int a[5]={1,2,3,4,5}; int p=(int)(&a+1); printf(“%d”,*(p-1)); 答案为什么是5?

a是一个数组名,也就是数组的首地址。对a进行取地址运算符,得到的是一个指向数组的指针!
也就相当于:int (p) [5] = &a;
p是一个指针,它指向的是一个包含5个int元素的数组!!
那么执行p+1后,p的偏移量相当于 p + sizeof(int) * 5 !!
而程序中强制将指针p转换成一个int
那么 p -1 其实就是 p - sizeof(int)
所以,p -1 指向了数组中得最后一个元素,也就是 5

以上是别人的
&a+1 ==> sizeof(a)=20byte 才是 +1 的长度,a[0]视为起始位置00,即现在p指向的位置是 00+20。之后被强转(int*)也就是此时 sizeof(int)=4byte ,*(p-1) 指向位置是 00+20-04=16 ;{1,2,3,4,5}位置为 0,4,8,12,16 ;因此是5

字符数组与字符串

两者区别在于 有没有’\0’;有’\0’的字符数组就是字符串
char a[6]="hello"这就一个数组。如果是在局部函数中是放在栈中的,不能返回c在主函数中打印的,因为局部函数结束,c[5]的生命周期也就结束了。
char *p=“hello”,是 字符串常量,存于rodata段,所以如果在局部函数中返回p也是可以的,在局部函数结束的时刻它把hello的rodata段中的地址返回,主函数中可以用的。

#include <stdio.h>
#include<string.h>
/*
个人理解:
char b1[2][4]: 开辟一块2*4*sizeof(char)大小内存,存放char数据;
char* say[3] = { "hi","hello","hey" };: 开辟一块3*sizeof(char*)大小内存,存放char*数据;"hi"等是字符串常量,存于rodata段。
若另有char* say2[] = { "hi","hello" }; 则里面的字符串地址相同,即共享相同字符串,节省空间。
*/
	char b1[2][4] = {"abc","bcd"};  //二维字符数组  
	char b2[][4] = { "abc","bcd","cde" };  
	printf("sizeof(b1)=%p\n", sizeof(b1));  //sizeof(b1)=8
	printf("sizeof(b2)=%p\n", sizeof(b2));  //sizeof(b2)=12
	
	char* say[] = { "hi","hello","hey" };  
	char* say2[] = { "hi","hello" };  // &say[0]==&say2[0]  &say[1]==&say2[1] 
	char* say3[] = { "hey","hello" };//&say3[0]==&say[2] &say[1]==&say3[1]
	printf("sizeof(say)=%p\n", sizeof(say));  //sizeof(say)=0000000C  ? 3*4
	printf("sizeof(say2)=%p\n", sizeof(say2));  //sizeof(say)=00000008  ?2*4
#if 1
	for (int i = 0; i < strlen(say); i++) {
		printf("say+%d=%p\t", i, say + i);           
		//say+0=0024F5DC say+1=0024F5E0 say+2=0024F5E4  +4byte是指针大小,say存放的是指针
		printf("*(say+%d)=%s\n", i, *(say + i));	//*(say+0)=hi *(say+1)=hello *(say+2)=hey
	}
#endif // 1
#if 1
	for (int i = 0; i < strlen(say);i++) {
		int j = 0;
		while (say[i][j]!='\0') {
			printf("say[%d][%d]=%c\t ",i,j,say[i][j]);
			printf("&say[%d][%d]=%p\n ", i, j, &say[i][j]);
			++j;
			/*
			say[0][0]=h      &say[0][0]=003F7B8C
			say[0][1]=i     &say[0][1]=003F7B8D
			say[1][0]=h     &say[1][0]=003F7B90
			say[1][1]=e     &say[1][1]=003F7B91
			say[1][2]=l     &say[1][2]=003F7B92
			say[1][3]=l     &say[1][3]=003F7B93
			say[1][4]=o     &say[1][4]=003F7B94
			say[2][0]=h     &say[2][0]=003F7B98
			say[2][1]=e     &say[2][1]=003F7B99
			say[2][2]=y     &say[2][2]=003F7B9A
			*/
		}
	}
#endif // 1
#if 1// 测试 say[][i]  
	for (int i = 0; i < 100;++i) {
//内存里面存放是连续的。 
//系统怎样分配内存不清楚,i可以加下去,加下去可以出现  say[0][%d]=%c等字符串,即系统把所有字符串都放在一块内存里
		printf("say[0][%d]=%c \t ",i, say[0][i]); //0       	        
		printf("&say[0][%d]=%p \n ", i, &say[0][i]); //0
	}
#endif
#if
	int a[] = { 1,2,3,4,5 }; int* p[] = { a,a + 1,a + 2,a + 3 }; 
	int** q = p;//int** q = &a[0] p+2->&p[2]   *(p+2)->p[2]  **(p+2)-> a+2
	int ans = *(p[0] + 1) + **(q + 2);
	//q->p首地址	*q->p[0]				**q->a[0]
	//				*(q+1)==>p[0]->p[1]     *(*q+1)  ==>a[0]->a[1]
	//				p->p[0]				    *p->a[]
	//				*(p+1)==>p[0]->p[1]		*p+1     ==>a[0]->a[1]
	//										*(p[0]+1)==>a[0]->a[1]
#endif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值