指针是c的灵魂,俺这里只能抛砖引玉了.

1 首先,数组名不能当作赋值对象,比如下面的代码:
char *s="abc";
	char *s1="bcd";
	s1=s;
	printf("%c\n",s1[0]);
可以正常运行,如果把 这边的指针变为数组就会出错。

2 下面阐述一下,指针和数组各自是如何访问的:
char s[]="abc"; c=s[i];


编译器符号表有一个符号 s 地址为 1234,然后首先取i的值,把i和1234相加,然后取出(i+1234)的内容付给c.

char *s="abc"; c=s[i];

编译器符号表有一个符号s,他的地址为1234,然后取地址1234的内容,就是'5678',然后把i和5678相加,然后取出(i+5678)的内容付给c.

大家可以看下下面的这个程序:
#include <stdio.h>
void main()
{
	char *s="abc";
	char s2[]="789";
	printf("%d\n",&s);
	printf("%d\n",&s[0]);
	printf("%d\n",&s2);
	printf("%d\n",&s2[0]);
}

呵呵,s和s[0] 的地址竟然不一样。

3 定义指针时编译器并不为指针所指向的内容分配空间,它只分配指针本身的空间,除非在声明的同时付给指针一个字符串常量初始化。比如:
char *s="abc";

可是只有对字符串常量才是如此,其他的类型都会出错。

4 数组和指针的相同点。
。表达式中的数组名(不同于声明)被编译器当做一个指向数组第一个元素的指针。
。下标总是和指针偏移量相同,a[i]总是被编译器改写成*(a+i)这种形式来访问。(比如:a[6]和6[a]是一样的)
。在函数参数的声明中,数组名被编译器当做一个指向数组第一个元素的指针
可以看下下面的代码的输出。
#include <stdio.h>
void f(char s[]);
void g(char *s);
char s2[4]="789";
void main()
{	
	printf("%d\n",&s2);
	printf("%d\n",&(s2[0]));
	f(s2);
	g(s2);
}
void f(char s[4])
{
	printf("%d\n",&s);
	printf("%d\n",&(s[0]));
}
void g(char *s)
{
	printf("%d\n",&s);
	printf("%d\n",&s[0]);
}

为什么c要做成这种呢,其实很简单,就是在c中调用函数的时候会把实参进行拷贝,而如果实参是数组的话,拷贝的开销太大,所以不如指针方便.

呵呵,这边多维数组没有涉及到,不过多维数组只要紧记不过是数组的数组罢了.
评论
kdekid 2007-05-22
Arath 写道
简单的说指针符号是一个实体,需要分配实际的内存空间给指针,这个空间里存着的是指针指向的数据的地址.
数组符号则不存在实体,一切都由编译器去解决.

前面kdekid提到的对齐问题不是绝对的,不同的编译器优化产生的结果不同,而且可以通过编译开关来改变.


现在的编译器默认都是对齐的。有的时候是必须对齐,这需要看ABI的具体实现。
Arath 2007-05-22
简单的说指针符号是一个实体,需要分配实际的内存空间给指针,这个空间里存着的是指针指向的数据的地址.
数组符号则不存在实体,一切都由编译器去解决.

前面kdekid提到的对齐问题不是绝对的,不同的编译器优化产生的结果不同,而且可以通过编译开关来改变.
kdekid 2007-05-22
homejet 写道
指针好难懂啊 ,最近正在看一本电子书<c和指针> www.51leifeng.net/thread-655-1-1.html,有兴趣的朋友可以去看看挺好的 大家可以一起交流啊


C and Pointer 是一本好书。不过事实上,更推荐多写代码,特别是 socket 编程,会更多的涉及到指针。写代码比单看书有用多了。
kdekid 2007-05-22
林杰杰 写道
以下是从《C专家编程》里总结出来的三条,加上了个人见解。
1.当数组整体作为参数传递时,数组被当成一个指针传递。于是,在被调用函数中,数组就是指针。
2.作为整体考虑时,数组就是数组,指针就是指针,两者不中互换。
2.1.数组声明时,指针与数组不可互换。包括数组的定义,声明,与extern声明,都不可互换。
2.2.应用于sizeof操作时,两者不可互换。
3.在引用数组单个元素的时候,数组仍然是数组,两者虽然本质不同,但是表现得跟const指针一样。因此如果arr是一个指针,在arr[i]中,arr可以用指针替换,不能改变arr的值,如++arr是不允许的。


这里总结得很好。有几点要补充的:
1、数组有单独的空间,一般就是 数组下标个数×sizeof(类型),例如在 32 位 intel 处理器上,char[10] 占用 10×1=10 字节空间。但因为数据需要对齐的原因,实际会占用 10+2 字节,因为要按处理器字长 4字节 对齐。
2、指针的大小一般就是机器地址总线长度,例如在 32 位 intel cpu 上就是 4 字节长。
3、空数组只是一个符号,不占内存空间。例如
struct udphdr {
  u_int16_t source;
  u_int16_t dest;
  u_int16_t len;
  u_int16_t check;
  char tag[0];
  char data[100];
};
struct udphdr udp;

这里,tag 不占内存,只表示 &((char*)&udp+8) 的内存地址位置,
&udp.tag == &((char*)&udp + 8 ) == &udp.data

所以
char tag[0];
通常用以标记 struct 中某一特定内存位置。
林杰杰 2007-05-22
以下是从《C专家编程》里总结出来的三条,加上了个人见解。

1.当数组整体作为参数传递时,数组被当成一个指针传递。于是,在被调用函数中,数组就是指针。
2.作为整体考虑时,数组就是数组,指针就是指针,两者不中互换。
2.1.数组声明时,指针与数组不可互换。包括数组的定义,声明,与extern声明,都不可互换。
2.2.应用于sizeof操作时,两者不可互换。
3.在引用数组单个元素的时候,数组仍然是数组,两者虽然本质不同,但是表现得跟const指针一样。因此如果arr是一个指针,在arr[i]中,arr可以用指针替换,不能改变arr的值,如++arr是不允许的。
simohayha 2007-05-22
Spike 写道
simohayha 写道
。下标总是和指针偏移量相同,a[i]总是被编译器改写成*(a+i)这种形式来访问。(比如:a[6]和6[a]是一样的)


可能是我专牛角尖了

*(a+i)通常会计算为a+T*i,这里T是数组存放的数据类型的大小。
“a[6]”和“6[a]”通常是不一样的

这里是你理解错了,在这边你所说的T是编译器进行判定的而不是我们来决定的,比如这边a[i]只是说从a所指的地址开始,前进i步,每步都是一个数据类型(也就是数组所存的数据类型),在这边每步的大小是编译器进行判断的。a[6]和6[a]编译器会自行判断 那个是数组名,那个是所要前进的步数.
virtualsolo 2007-05-22
simohayha 写道
指针是c的灵魂,俺这里只能抛砖引玉了.

char *s="abc"; c=s[i];

编译器符号表有一个符号s,他的地址为1234,然后取地址1234的内容,就是'5678',然后把i和5678相加,然后取出(i+5678)的内容付给c.


实际上可以这么理解,变量的名称对应了内存的一块区域。对于普通变量如 int i = 1,i 对应的内存存储的值就是“1”,而对于指针类型的变量 char *s = "abc",s对应内存存储的值实际上是一个地址,通过该地址可以引用到实际的值(也就是书上说的“指向xxx”)。
用simohayha上面的例子:s存储的东西是'5678',这个地址就是字符串“abc”的实际存放位置。而s本身也是有地址的,通过 &s得到的就是'1234'
Spike 2007-05-22
simohayha 写道
。下标总是和指针偏移量相同,a[i]总是被编译器改写成*(a+i)这种形式来访问。(比如:a[6]和6[a]是一样的)


可能是我专牛角尖了

*(a+i)通常会计算为a+T*i,这里T是数组存放的数据类型的大小。
“a[6]”和“6[a]”通常是不一样的
七猫 2007-05-21
不可以把数组名字作为左值运算
其他情况下基本一样的吧。
jigsaw 2007-05-20
the truth is that c89 requires compilers to put char *a="aaaaa"; into .text segment.
now you should know why it can never be ruined by other stack frames, and why any attempt of modifying the content of the string will trigger segment fault.
simohayha 2007-05-19
刑天战士 写道


char* s="xxxxxx";

s的实际地址指向常量区的值为xxxxxx的地址,也就是说,s是一个字符串常量

看这段:
#include <stdio.h>
#include <stdlib.h>

char* s1();
char* s2();
int main()
{
    printf("%s",s1());
    printf("%s",s2());
}

char* s1()
{
    char *a="aaaaa";
    return a;
}

char* s2()
{
    char a[]="bbbbbbb";
    printf("%s",a);
    return a;
}


s1的方法返回的是正确的字符串,s2就不敢保证了,因为他的char a[]="bbbbbbb";只是在当前栈中,a返回的是个可能无效的地址(我是说可能,因为编译器可能那个时候没有覆盖这段内存)


字符串应该是个隐式的static吧?
因为a[]是个自动变量,一出这个函数体,这个变量就会被销毁,此时谁也不知道a所指的内容是什么,那块内存不知道什么时候会被覆盖.
xin_wang 2007-05-18
刑天战士 写道
xin_wang 写道
hurricane1026 写道
s和s[0]竟然不同,为什么呢?
写程序测试了一下,对于char[]确实是这样,但是对于int[],double[]都是相同的。simon测试了么?

这个是我用gcc编译运行后的结果
2280676
4202496
2280672
2280672
貌似这个char *s只在栈里分配一个指针的空间,指向实际的字符串地址(这个字符串放在哪里的? ),所以s和&s[0]的地址是不同的


char* s="xxxxxx";

s的实际地址指向常量区的值为xxxxxx的地址,也就是说,s是一个字符串常量

其实我就是不确定是不是有常量区这个概念
惭愧啊
刑天战士 2007-05-18
看这段:
#include <stdio.h>
#include <stdlib.h>

char* s1();
char* s2();
int main()
{
    printf("%s",s1());
    printf("%s",s2());
}

char* s1()
{
    char *a="aaaaa";
    return a;
}

char* s2()
{
    char a[]="bbbbbbb";
    printf("%s",a);
    return a;
}


s1返回的是正确的字符串,因为s1引用的是不变的常量区;s2则是引用的栈的内存,所以s2有可能出现不正确的结果(在我的gcc上,是乱码),但是在s2内部没有问题,因为在s2内部,指向栈的引用是有效的
刑天战士 2007-05-18
xin_wang 写道
hurricane1026 写道
s和s[0]竟然不同,为什么呢?
写程序测试了一下,对于char[]确实是这样,但是对于int[],double[]都是相同的。simon测试了么?

这个是我用gcc编译运行后的结果
2280676
4202496
2280672
2280672
貌似这个char *s只在栈里分配一个指针的空间,指向实际的字符串地址(这个字符串放在哪里的? ),所以s和&s[0]的地址是不同的


char* s="xxxxxx";

s的实际地址指向常量区的值为xxxxxx的地址,也就是说,s是一个字符串常量

看这段:
#include <stdio.h>
#include <stdlib.h>

char* s1();
char* s2();
int main()
{
    printf("%s",s1());
    printf("%s",s2());
}

char* s1()
{
    char *a="aaaaa";
    return a;
}

char* s2()
{
    char a[]="bbbbbbb";
    printf("%s",a);
    return a;
}


s1的方法返回的是正确的字符串,s2就不敢保证了,因为他的char a[]="bbbbbbb";只是在当前栈中,a返回的是个可能无效的地址(我是说可能,因为编译器可能那个时候没有覆盖这段内存)
iunknown 2007-05-18
hurricane1026 写道
s和s[0]竟然不同,为什么呢?


s 和 s[0] 类型不同

char *s="abc";

&s --> char **
&s[0] --> char *

对于普通数组,s 应该和 &s[0] 相同
xin_wang 2007-05-18
simohayha 写道
呵呵,增加1 难道增加的不是 sizeof(char)吗? 你可以试试 int指针,看看他是增加多少.

靠,快下班脑子坏了,其实看看上面s到s2的增长就能看出来,确实是增长了sizeof(char),丢人了……
simohayha 2007-05-18
呵呵,增加1 难道增加的不是 sizeof(char)吗? 你可以试试 int指针,看看他是增加多少.
xin_wang 2007-05-18
hurricane1026 写道
s和s[0]竟然不同,为什么呢?
写程序测试了一下,对于char[]确实是这样,但是对于int[],double[]都是相同的。simon测试了么?

这个是我用gcc编译运行后的结果
2280676
4202496
2280672
2280672
貌似这个char *s只在栈里分配一个指针的空间,指向实际的字符串地址(这个字符串放在哪里的? ),所以s和&s[0]的地址是不同的
而char[]是在栈里分配一个字符串,所以&s2[0]和s2的地址是同一个。

在那个程序后面加几行:
printf("%d\n",&s2[1]);
printf("%d\n",&s2[2]);
printf("%d\n",&s2[3]);

就能观察到这个字符串的在栈中的地址是在往下增长的:
2280676
4202496
2280672
2280672
2280673
2280674
2280675
但是好像不是不是往下增长一个sizeof(char)而是一个sizeof(int),可能是因为优化的原因,具体就不清楚了
simohayha 2007-05-18
只要是数组都是相同的,因为指针的话你需要先取出指针的地址然后再从这个地址取出内容(也就是指针所指的数组的第一个元素的地址),然后再取出这个地址的内容.
simohayha 2007-05-18
int ,double也是不同的。
#include <stdio.h>
void main()
{
	int s[]={1,2,3};
	int *d=s;
	printf("%d\n",&d);
	printf("%d\n",&d[0]);
}
simohayha
搜索本博客
存档
最新评论