指针数组的内存结构

来源:互联网 发布:ubuntu没有eth0 编辑:程序博客网 时间:2024/04/28 04:20

1、指针数组

2、赋值

 

1、指针数组

 

const char *g_sHelpCommSet[][2]={{"1","2323"},{"3","4dfdsfd"}};

对于这个数组来看看系统给它分配的内存空间是什么样子的?!

 

写个address.c

#include <stdio.h>

 

const char *g_sHelpCommSet[][2]={{"1","2323"},{"3","4dfdsfd"}};

 

int main( void )

{

return 0;

};

 

编译,带-g参数,方便在gdb中调试

$ gcc -g -o test test.c

 

然后调试,查看内存,系统是怎么给g_sHelpCommSet安排内存的

$ gdb address

GNU gdb 6.6-debian

Copyright (C) 2006 Free Software Foundation, Inc.

GDB is free software, covered by the GNU General Public License, and you are

welcome to change it and/or distribute copies of it under certain conditions.

Type "show copying" to see the conditions.

There is absolutely no warranty for GDB. Type "show warranty" for details.

This GDB was configured as "i486-linux-gnu"...

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

/* 在程序入口处设制断点 */

(gdb) break main

Breakpoint 1 at 0x8048352: file address.c, line 7.

/* 运行程序 */

(gdb) run

Starting program: /home/guaicat/workroom/memory/address

 

Breakpoint 1, main () at address.c:7

7 return 0;

(gdb)

/* 看g_sHelpCommSet 数组在内存中的模样 */

(gdb) x/12x g_sHelpCommSet

0x804954c <g_sHelpCommSet>: 0x0804842c 0x0804842e 0x08048433 0x08048435

0x804955c <completed.5758>: 0x00000000 0x00000000 0x00000000 0x00000000

0x804956c: 0x00000000 0x00000000 0x00000000 0x00000000

/* 注意:g_sHelpCommSet是0x804954c, 0x0804842c是数组里面的值 */

/* 继续看,g_sHelpCommSet 指向的内存单元 */

(gdb) x/24b 0x0804842c

0x804842c: 0x31 0x00 0x32 0x33 0x32 0x33 0x00 0x33

0x8048434: 0x00 0x34 0x64 0x66 0x64 0x73 0x66 0x64

0x804843c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

 

对于系统或者程序来说,因为char *指针类型,所以它认识的g_sHelpCommSet 仅仅是一个指针, 0x804954c;记住了,g_sHelpCommSet就是 0x804954c,而且仅仅是 0x804954c!!!

 

指针数组,对于系统或者程序来说,依然是一个指针,但是因为数组的下标,程序会为这个指针数组设制偏移量来解释,比如申明的g_sHelpCommSet[][2]中,g_sHelpCommSet[i][j]就被解释为 g_sHelpCommSet + (sizeof(char*)*2)*i + (sizeof(char*))*j 的地址,因为sizeof(char *)==4,所以

 

g_sHelpCommSet[0][0] = 0x804954c + (4*2)*0 + (4)*0 = 0x804954c

g_sHelpCommSet[0][1] = 0x804954c + (4*2)*0 + (4)*1 = 0x8049550

g_sHelpCommSet[1][0] = 0x804954c + (4*2)*1 + (4)*0 = 0x8049554

g_sHelpCommSet[1][1] = 0x804954c + (4*2)*1 + (4)*1 = 0x8049558

 

因为数组记录的是首地址,靠下标来算偏移量来决定某个数组成员的实际内存地址,这跟指针里面的地址操作是一样的,所以,表达一个数组char array[7]的成员,可以用array[3]这种方式,也可以用(array+3)来表达,它们都是array的地址+sizeof(char)*3的那个内存单元:)

 

我们知道这个指针数组了,再看看指针数组每个指向的内存单元内容

(gdb) x/12x g_sHelpCommSet

0x804954c <g_sHelpCommSet>: 0x0804842c 0x0804842e 0x08048433 0x08048435

0x804955c <completed.5758>: 0x00000000 0x00000000 0x00000000 0x00000000

0x804956c: 0x00000000 0x00000000 0x00000000 0x00000000

 

可以看出来:

g_sHelpCommSet[0][0],即0x804954c里面存着 0x0804842c

g_sHelpCommSet[0][1],即0x8049550里面存着 0x0804842e

g_sHelpCommSet[1][0],即0x8049554里面存着 0x08048433

g_sHelpCommSet[1][1],即0x8049558里面存着 0x08048435

 

这些又是什么呢?这些就是g_sHelpCommSet成员所表达的字符串的首地址

(gdb) x/24b 0x0804842c

0x804842c: 0x31 0x00 0x32 0x33 0x32 0x33 0x00 0x33

/* '1' '/0' '2' '3' '2' '3' '/0' '3' */

0x8048434: 0x00 0x34 0x64 0x66 0x64 0x73 0x66 0x64

/* '/0' '4' 'd' 'f' 'd' 's' 'f' 'd' */

0x804843c: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

/* '/0' */

 

0x0804842c指向的字符串是 0x31 0x00,即 “1”

0x0804842e指向的字符串是 0x32 0x33 0x32 0x33 0x00,即 ”2323”

0x08048433指向的字符串是 0x33 0x00,即 ”3”

0x08048435指向的字符串是 0x34 0x64 0x66 0x64 0x73 0x66 0x64 0 x00,即 ”4dfdsdf”

 

由此,我们可以总结出下图:

 

2、赋值

 

如果有一个新的 char **info,我们想把g_sHelpCommSet[0],即{“1”,”2323”}赋值给它,怎么做?!

 

由上图,我们可以看出,{“1”,”2323”},我们只需把g_sHelpCommSet[0][0]中存放的指针赋值给info[0]即可,每个指针占4个字节,所以两个指针占8个字节(存放{“1”,”2323”}占用了内存中的7个字节);

而char **info,它被系统认为只是一个地址!即使它使出浑身解数,也只能指向一个内存单元,所以,我们只要给info申请8个内存空间,用来存储它两个成员指针

 

info = (char**)malloc( sizeof(char*)*2 );

info[0] = g_sHelpCommSet[0][0];

info[1] = g_sHelpCommSet[0][1];

 

现在,info有2个成员指针,跟g_sHelpCommSet[0][0]和g_sHelpCommSet[0][1]一样,分别指向0x0804842c和0x0804842e,就指向 {“1”,”2323”} 了,其内存结构与图中表达的一样,看下面的内存状态图:

 

看程序例子

$ more address.1.c

#include <stdio.h>

#include <malloc.h>

 

const char *g_sHelpCommSet[][2]={{"1","2323"},{"3","4dfdsfd"}};

char **info;

 

int main( void )

{

info = (char**)malloc( sizeof(char*)*2 );

 

info[0] = (char*)g_sHelpCommSet[0][0];

info[1] = (char*)g_sHelpCommSet[0][1];

 

printf( "info[0] = %s/n", info[0] );

printf( "info[1] = %s/n", info[1] );

 

free( info );

return 0;

};

$ gcc -o address.1 address.1.c

$ ./address.1

info[0] = 1

info[1] = 2323

$

 

 

这可能不是你当初希望的,我们只是让info指向g_sHelpCommSet[0],这么多赋值运算不说,没有想到还有malloc和free来麻烦; dorainm,能不能帮忙想一个新的办法,让info直接借用g_sHelpCommSet[0]的2个成员变量的内存空间,而这2个内存空间已经直接指向好了{“1”,”2323”}

回答是,当然可以!

 

char **info = g_sHelpCommSet[0];

这种写法,编译时候会给予警告的,因为info只是一个点,当它用info[1],就是访问info+sizeof(char*)*1的内存单元的时候,其实那个单元不是它的地盘,因为C语言没有对下标越界进行强制保护,只要不理那个警告,程序还是可以正常工作的!

 

内存状态:

 

程序例子:

 

$ more address.2.c

#include <stdio.h>

 

const char *g_sHelpCommSet[][2]={{"1","2323"},{"3","4dfdsfd"}};

char **info = g_sHelpCommSet[0];

 

int main( void )

{

printf( "info[0] = %s/n", info[0] );

printf( "info[1] = %s/n", info[1] );

 

return 0;

};

$ gcc -o address.2 address.2.c

address.2.c:4: warning: initialization from incompatible pointer type

$ ./address.2

info[0] = 1

info[1] = 2323

$

原创粉丝点击