Do you really understand pointers and arrays?

If you already understand pointers and arrays, let's take a look and give you some guidance. If you are not so clear yet, come and take a look. I hope it will be helpful to you.

In the previous study, we have learned that the array name represents the address of the first element of the array in most cases , except for the following two

1. &arrayname gets the address of the entire array, which is numerically the same as the address of the first element, but has different meanings.

2. The array name in sizeof (array name) represents the entire array, and the size of the entire array is calculated.         

When interpreting the following questions, first check whether these two conditions are met before making a judgment.             


test.1 (integer array)

#include<stdio.h>
#include<string.h>
int main()
{
	int a[] = { 1,2,3,4 };

	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));

	return 0;
}

parse 

    sizeof(a)//数组名单独放在sizeof内部,表示整个数组大小 4*4
	sizeof(a + 0)//后面+0,使得数组名没有单独,所以a就是首元素地址 4/8
    sizeof(*a)//首元素地址解引用,表示首元素 4
	sizeof(a + 1)//首元素地址+1表示第二个元素地址 4/8
	sizeof(a[1])//第二个元素 4
	sizeof(&a)//整个数组的地址 4/8
	sizeof(*&a)// &a是整个数组的地址,*访问整个数组元素 16(这里*与& 的效果可以抵消)
	sizeof(&a + 1)//整个数组的地址+1,跳过4个整型 4/8
	sizeof(&a[0])//地址 4/8
	sizeof(&a[0] + 1)//第二个元素的地址 4/8

compile 


text.2 (character array one)

int main()
{
	
	char arr[] = { 'a','b','c','d','e','f' };

	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));

	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));

	return 0;
}

parse

    strlen(arr)//计算\0之前的字符 随机数
    strlen(arr + 0)//随机数
    strlen(*arr)//arr是首元素地址,*arr就是arr[0](一个字符,strlen接受的是地址)而你将数组首元                
    //素传(a)过去,编译器就会自动将a的ASCLL码值作为地址传过去导致非法访问
    strlen(arr[1])// 非法访问
    strlen(&arr)//&arr得到的是整个数组的地址,类型是char(*)[6]而strlen是 
    //size_t strlen( const char *string );所以传参时就强行转换了类型 随机数
    strlen(&arr + 1)//&arr+1 跳过整个char[6]的字符型 随机数-6
    strlen(&arr[0] + 1)//第二个元素的地址 随机值-1

    sizeof(arr)//sizeof计算的是占用内存空间大小,不关心内存存放的到底是什么值 6
    sizeof(arr + 0)//地址 4/8
    sizeof(*arr)//arr[0] 1
    sizeof(arr[1])// 1
    sizeof(&arr)//地址 4/8
    sizeof(&arr + 1)//地址 4/8
    sizeof(&arr[0] + 1)//地址 4/8

compile

test.2 (character array two)

int main()
{
	
	char arr[] = "abcdef";

	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));

	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));

	return 0;
}

parse 

 The difference between this string and the previous string is that there is one more \0, and the analysis method is the same as above.

compile

test.2 (character array three)

int main()
{
	char* p = "abcdef";

	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));
	printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));
	printf("%d\n\n", strlen(&p[0] + 1));


	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));

	return 0;
}

parse

The string here is a constant string, and p stores the address of the first character of this constant string.

    strlen(p)// 6
    strlen(p+1)// 5
    strlen(*p)// *p就是字符a 非法访问
    strlen(p[0])// 字符a 非法访问
    strlen(&p)//指针变量p存放字符a的地址,&p就是存放字符a地址的地址 随机值
    strlen(&p+1)//&p+1是在&p的基础上跳过一个指针类型(x64位机器8个字节) 随机值
    //与上面的随机值不一定差值为8,取决于\0的位置
    strlen(&p[0]+1)//p[0]可以看作 *(p+0)  5

    sizeof(p)//p是字符a的地址 4/8
    sizeof(p+1)//字符b的地址 4/8
    sizeof(*p)//字符a 1
    sizeof(p[0])//字符a 1
    sizeof(&p)// 地址 4/8
    sizeof(&p+1)//4/8
    sizeof(&p[0]+1)//地址 4/8

compile


 test.3 (two-dimensional array)

int main()
{
	int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };

	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));

	return 0;
}

parse

We first have to understand two-dimensional arrays. Two-dimensional arrays are actually one-dimensional arrays. They are stored continuously in the memory space and are not stored in the memory in the form of two-dimensional space.

Each row of a two-dimensional array can be regarded as the array name of a one-dimensional array. The array name of a two-dimensional array is the address of the first row element.

    sizeof(a)//a是二维数组的数组名,单独放在sizeof内部 12*4
    sizeof(a[0][0])//第一个元素 4
    sizeof(a[0])//第一行元素的数组名,单独放在sizeof内部 4*4
    sizeof(a[0] + 1)//第一行首元素的地址+1 就是第一行第二个元素的地址 4/8
    sizeof(*(a[0] + 1))//第一行第二个元素 4
    sizeof(a + 1)//首行元素的地址+1 第二行元素地址 4/8
    sizeof(*(a + 1))//第二行元素地址解引用 4*4
    sizeof(&a[0] + 1)//a[0]是第一行元素的数组名,取地址就是第一行地址,+1就是第二行地址 4/8
    sizeof(*(&a[0] + 1))第二行地址解引用 4*4
    sizeof(*a)//a是首行地址,解引用就是首行元素 4*4

    sizeof(a[3])//第四行元素数组名,这不越界了吗?

sizeof(a[3]) seems to be an array out of bounds, and an error should be reported, but as for the sizeof operator, it does not execute (access) the expression inside the brackets, it only needs to know the internal data Type on it.

There is no operation on the internal expression of sizeof here, so you only need to know the internal data type of sizeof to get the result.

The main reason inside the code is because the source code of test.c we wrote————>encoding——————>link——————>generate the test.exe executable program for running. The above expression needs to be calculated to generate the test.exe executable program for running. The calculation results of sizeof do not require your calculation results at all. The results are already obtained during your compilation process.

Any value has two attributes: 1. Value attribute 2. Type attribute

 So back to the question a[3] above, although it is out of bounds, no out-of-bounds access has occurred. You only need to get the type attribute.

compile 


test.4(pointer)

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };

	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));

	return 0;
}

parse

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };//整型数组存放五个元素

	int* ptr = (int*)(&a + 1);//&a是整个数组的地址,+1跳过五个整型
	printf("%d,%d", *(a + 1), *(ptr - 1));//a是首元素地址,ptr是整形指针-1是移一个整形

	return 0;
}

Compile (structure pointer)


test.5 

struct Test
{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}* p;
//已知,结构体Test类型的变量大小是20个字节
int main()
{
    p = (struct Test*)0x100000;
    printf("%p\n", p + 0x1);
    printf("%p\n", (unsigned long)p + 0x1);
    printf("%p\n", (unsigned int*)p + 0x1);

    return 0;
}

parse

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p;
//已知,结构体Test类型的变量大小是32个字节
int main()
{
	p = (struct Test*)0x100000;//将0x100000强行存放在指针p中
	printf("%p\n", p + 0x1);//0x1就是十进制的1,p是结构体指针类型,+1跳过一个结构体类型 32
	printf("%p\n", (unsigned long)p + 0x1);//将p强转成无符号整型+1就是整数+1(可不是整形指针)
	printf("%p\n", (unsigned int*)p + 0x1);//将p强转成无符号整型指针类型,+1跳过一个整型

	return 0;
}

compile

 20 in hexadecimal is 32 in decimal


test.5 

#include <stdio.h>

int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

parse 

    int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);//&a实际是数组指针类型,强制转换成整形指针存储,&a是整个数组的地址,+1跳过整个数组

	int* ptr2 = (int*)((int)a + 1);//将a(首元素地址)强制转换成整型,+1就是按照整数+1,所以就地址向后跳一个字节

	printf("%x,%x", ptr1[-1], *ptr2);//%x打印十六进制数,*ptr2表示向后访四个字节

compile

The blogger actually compiled it in the x86 environment on VS:

 Since *ptr2 is actually 0x02 00 00 00 printed in the form of %x, the preceding 0x0 is omitted.


 test.6

#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

parse

int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//这个表示的是三行两列的二维数组,存放的是01 23 45吗?
//
//这可不能粗心大意哈,再仔细检查一下表达形式是否有问题。
//如果是是上面解释的不是这样嘛:{ {0, 1}, {2, 3}, {4, 5} }
//小括号不就成了逗号表达式嘛。存放 13 50 00

compile


  test.7

#include <stdio.h>
int main()
{
	int a[5][5];
	int (*p)[4];
	p = a;

	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);

	return 0;
}

parse

In order to better display it through pictures, the blogger organized the array into a two-dimensional form, which is actually stored in order in the memory.

//Here we need to know arr[i] = *(arr+i) 

    int a[5][5];

	int (*p)[4];//创建数组指针p
	p = a;//p存放a(二位数组首行元素的地址),数组指针p存放的是数组(存四个整型)
而a数组的首行有五个整形,所以发生了强制性存储。p存的就是a首行的前四个元素的地址。

	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//&p[4][2]中的p[4][2]可看作 *( *(p+4)+2 ) 而p是数组指针,+1就跳过一个数组(四个整型)
再解引用就相当于一个一维数组的数组名,在此基础再+2就是跳过两个整型

// &p[4][2] - &a[4][2] 的整数结果就是 -4
//而以地址的形式就是:将-4看作是地址,而-4在内存中是 0xFF FF FF FC(补码的十六进制)

 compile


   test.8

int main()
{
	char* a[] = { "work","at","alibaba" };

	char** pa = a;

	pa++;
	
	printf("%s\n", *pa);
	return 0;
}

parse

//The character pointer array here stores the address of the first element of each word.

 //When you encounter a question like this where the answer is a bit vague and uncertain, drawing analysis is a good choice.

compile

 


    test.9

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;

	printf("%s\n", **++cpp); 
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);

	return 0;
}

parse

First draw the corresponding analysis diagram:

  Execute the first printf:

printf("%s\n", **++cpp);//我们++先执行,而且极为关键的是++是赋值操作符
                           执行之后会改变cpp的值。

//cpp存放的是二级指针数组cp的首元素地址,++后就使cpp存放的是cp的第二个元素的地址

//第一次* 得到cp的第二个元素(c+2),
再次解引用就得到指针数组c的第三个元素(POINT的首元素地址)

The direction of the analysis chart changes 

  

Execute the second printf:

printf("%s\n", *-- * ++cpp + 3);//单目操作符优先级高于双目操作符
//*与++ --的操作符结合方向从右往左
//++操作符,自加1,指向cp[2]的地址,解引用指向cp[2]的空间
//因为cp[2]指向c[1]的地址,再--操作符,自减1,使得cp[2]指向c[0]的地址
//再解引用操作执行c[0](存放ENTER的首元素E的地址),+3指向ER

The direction of the analysis chart changes  

   Execute the third printf:

printf("%s\n", *cpp[-2] + 3);//cpp[-2]等价于 *(cpp-2) 此时就指向cp[0]空间
(cpp自生没有发生改变)
//再次解引用就表示c[3]这块空间(指向FIRST中F的地址)
//+3操作执行S的地址

The analysis chart points unchanged

   

Execute the fourth printf: 

printf("%s\n", cpp[-1][-1] + 1);//代码可看作 *( *(cpp-1) -1) + 1
// *(cpp-1)表示的是:此时指向cp[1]这块空间,而cp[1]指向c[2]的地址
//再 -1操作表示指向c[1]的地址,再次解引用表示c[1]这块空间(NEW首元素地址)
// +1指向NEW中E的地址

The analysis chart points unchanged

compile 

Guess you like

Origin blog.csdn.net/C_Rio/article/details/129331292