镜缘浮影 小人本住在 苏州的城外 家里有屋又有田 生活乐无边

C语言深度解剖 (四)

2016-12-06
wilmosfang
c
原文地址 http://soft.dog/2016/12/06/c-deep-04/

前言

C语言的水深不见底,好在一些前辈们已经将很多雷区探了一遍

这里分享一下我在学习 《C语言深度解剖》 过程中的一些笔记和心得


概要


指针与数组

#include <stdio.h> 

void main()
{
	int i=10; //&i 为 0x0018ff44
	int *p=&i;
	p=(int *)0x0018ff44;
	*p=11; 
	*(int *)0x0018ff44=12;
	printf ("%x\t%d\t%d\n",p,*p,*(int *)0x0018ff44); //18ff44 12 12
}

在VC++6.0中,上面的例子,数值变化都如预期,但是下面的数值变化会让人迷惑不解,因为p指针的值会随内容而变化(这个是不应该发生的事情,这是VC++6.0的bug)

#include <stdio.h> 

void main()
{
	int *p;
	p=(int *)0x0018ff44;
	*p=11; 
	*(int *)0x0018ff44=12;
	printf ("%x\t%d\t%d\n",p,*p,*(int *)0x0018ff44); 
}

不过,像这样,未经申请而直接使用内存的行为是很危险的

数组大小

void main()
{
	int a[5]={1,2,3,4,5};
	int i=sizeof(&a); //20 这是一个地址,但为什么是20个字节的长度呢
	int j=sizeof(a); //20 a作为一个首地址,为什么是20个字节的长度呢
	int k=sizeof(&a[0]); //4
	int l=sizeof(&a[5]); //4  及便这个元素不存在
}

可以将它们的地址分别取出来看看

首地址

#include <stdio.h> 

void main()
{
	int a[5]={1,2,3,4,5};
	int *ptr=(int *)(&a+1); // 一次性跳数组长度的字符个数
	printf("%d,%d\n",*(a+1),*(ptr-1)); // 2,5 
	printf("%x,%x,%x,%x,%x\n",a,&a,a+1,&a+1,ptr-1); //18ff34,18ff34,18ff38,18ff48,18ff44
}

对指针进行加1操作,得到的是连续内存中下一个元素的地址

一个类型为T的指针的移动,是以sizeof(T)为移动单位的

&a 与 a 的值是一样的,但是意思不一样

&a 是数组(结构体/构造体)的首地址

a 是数组首元素的首地址

&a+1,取数组a的首地址,该地址的值加上sizeof(a)的值,即&a+5*sizeof(int)

可以将a理解为结构体名,同时其值为&a[0]

a+1,是数组下一元素的首地址,即 a[1]的首地址

#include <stdio.h> 

void main()
{
	char a[5]={'a','b','c','d','e'};
	char (*p1)[3]=&a;
	char (*p2)[3]=a;
	char (*p3)[10]=&a;
	char (*p4)[10]=a;
	printf("%x,%x,%x,%x,%x\n",a,p1+1,p2+1,p3+1,p4+1); //18ff40,18ff43,18ff43,18ff4a,18ff4a
}

数组指针,是一个指针 ,但是它每次加一后,跳过的内存字节数为 n*sizeof(type)

地址的强制转换

#include <stdio.h> 

void main()
{
	int a[4]={1,2,3,4};
	int *ptr1 =(int *)(&a+1);
	int *ptr2=(int *)((int)a+1);
	printf("%x,%x\n",ptr1[-1],*ptr2); // 4,2000000
}

二维数组

#include <stdio.h> 

void main()
{
	int a[3][2]={(0,1),(2,3),(4,5)};
	int *p;
	p=a[0];
	printf("%d\n",p[0]); //1
}

这里千万要看清楚小括号里逗号分隔的表达式

#include <stdio.h> 

void main()
{
	int a[5][5];
	int (*p)[4];
	p=a;
	printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4][2]); // a_ptr=0X0018FF3C,p_ptr=0X0018FF2C
	printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]); //FFFFFFFC,-4
}

&a[4][2] 表示 &a[0][0]+4*5*sizeof(int)+2*sizeof(int)

&p[4][2] 表示 &a[0][0]+4*4*sizeof(int)+2*sizeof(int)

所以差 4*sizeof(int) ,而地址相减获得的是此类型数据占用内存的单位数,所以,虽然相差16个字节,但是相差4个单位,一个int占4字节

原文地址 http://soft.dog/2016/12/06/c-deep-04/

评论