`
simohayha
  • 浏览: 1403321 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

指针与数组的异同

阅读更多
指针是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中调用函数的时候会把实参进行拷贝,而如果实参是数组的话,拷贝的开销太大,所以不如指针方便.

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

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


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

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


C and Pointer 是一本好书。不过事实上,更推荐多写代码,特别是 socket 编程,会更多的涉及到指针。写代码比单看书有用多了。
18 楼 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 中某一特定内存位置。
17 楼 林杰杰 2007-05-22  
以下是从《C专家编程》里总结出来的三条,加上了个人见解。

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


可能是我专牛角尖了

*(a+i)通常会计算为a+T*i,这里T是数组存放的数据类型的大小。
“a[6]”和“6[a]”通常是不一样的
13 楼 七猫 2007-05-21  
不可以把数组名字作为左值运算
其他情况下基本一样的吧。
12 楼 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.
11 楼 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所指的内容是什么,那块内存不知道什么时候会被覆盖.
10 楼 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是一个字符串常量

其实我就是不确定是不是有常量区这个概念
惭愧啊
9 楼 刑天战士 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内部,指向栈的引用是有效的
8 楼 刑天战士 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返回的是个可能无效的地址(我是说可能,因为编译器可能那个时候没有覆盖这段内存)
7 楼 iunknown 2007-05-18  
hurricane1026 写道
s和s[0]竟然不同,为什么呢?


s 和 s[0] 类型不同

char *s="abc"; 

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

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

靠,快下班脑子坏了,其实看看上面s到s2的增长就能看出来,确实是增长了sizeof(char),丢人了……
5 楼 simohayha 2007-05-18  
呵呵,增加1 难道增加的不是 sizeof(char)吗? 你可以试试 int指针,看看他是增加多少.
4 楼 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),可能是因为优化的原因,具体就不清楚了
3 楼 simohayha 2007-05-18  
只要是数组都是相同的,因为指针的话你需要先取出指针的地址然后再从这个地址取出内容(也就是指针所指的数组的第一个元素的地址),然后再取出这个地址的内容.
2 楼 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]);
}

相关推荐

    语言详解指针和数组PPT学习教案.pptx

    - 指针数组和数组指针是两种不同的概念,前者是一个数组,每个元素都是一个指针,后者是指向数组的指针,指向一个连续存储的元素集合。 **4. 动态数组和多维数组** - 多维数组在内存中是连续的,第一维的大小可以...

    C++之数组与指针的异同 pdf

    - **多维数组的降维处理**:在C++中,多维数组可以被视为指针数组,其中每个元素都是指向单维数组的指针。 5. **语法糖**:在很多情况下,C++允许将数组名和指针进行相似的操作,例如解引用和递增操作,但这仅仅是...

    易语言源码易语言取数组内容异同源码.rar

    此外,为了提高效率,我们可能需要考虑使用某种算法来优化比较过程,比如使用哈希函数快速检查元素的相等性,或者使用双指针技术同时遍历两个数组。 通过学习并理解这个源码示例,你可以掌握易语言中数组操作的基本...

    c语言与指针 个人觉得最好用的书

    本资源主要涉及C语言与指针相关的知识点,涵盖了指针和数组、字符串、内存操作、数据结构及算法、标准预定义宏、断言、typedef、变量的存储类型及初始化、左值和右值、结构的自引用、整数溢出等方面的内容。...

    c++指针面试题经典

    4. **指针与引用**:比较指针和引用的异同,它们都是间接访问变量的方式,但引用不具有NULL状态,且一旦初始化后不能改变引用的对象。 5. **动态内存管理**:使用`new`和`delete`操作符动态分配和释放内存,以及...

    易语言数组比较模块

    6. **比较数组不同_子程序指针**:子程序指针数组比较可能涉及函数地址的对比,这在多线程编程、动态链接库调用或插件系统中很有用,确保函数指针指向正确的执行代码。 7. **比较数组不同_日期时间**:日期时间数组...

    C++指针与内存管理.doc

    最后,文章讨论了指针和数组的异同。数组在静态存储区或栈上创建,其地址和大小固定,内容可变;而指针可以指向任意类型内存,更加灵活,但也更容易引发错误,尤其是当尝试修改常量字符串时。 通过理解这些基本概念...

    电子书C语言深度解剖

    3. 指针与数组:对比分析指针与数组的异同,展示如何通过指针操作数组。 五、结构体与联合 1. 结构体:定义结构体类型,组合不同类型的数据,创建结构体变量,以及结构体在内存中的布局。 2. 联合:理解联合的概念...

    彻底搞定C指针(完全版·修订增补版)

    数组名与指针变量的区别**:详细分析了数组名与指针变量之间的异同,这对于理解指针和数组的关系至关重要。 - **4. 声明指针常量**:介绍如何声明指针常量,并讨论其在实际编程中的作用。 ##### 第肆篇:`const ...

    语言指针PPT学习教案.pptx

    此外,指针变量作函数参数与普通变量作函数参数的异同在于,普通变量传递的是值的副本,而指针变量传递的是地址,这意味着函数可以改变指针指向的变量的值,而不会影响到函数外部的其他变量。 学习指针是深入理解...

    你必须知道的495个C语言问题(完整版,含所有章节)

    - 指针与数组:对比指针与数组的异同,掌握通过指针操作数组的方法。 - 指针与函数:理解指针作为函数参数,以及返回指针的函数的应用。 3. **预处理与宏定义** - 预处理器宏:了解#define用于创建常量、函数宏...

    the C programming language

    - **指针数组与指向指针的指针**:解释多级指针的定义与使用。 - **多维数组**:介绍多维数组的概念及其声明方法。 - **多维数组的初始化**:讲解如何初始化多维数组。 - **指针与多维数组的对比**:比较指针与...

    the c programing language

    - **指针数组与指向指针的指针**:讲解指针数组的概念及复杂指针类型的使用。 - **多维数组**:介绍多维数组的定义及其与指针的关系。 - **多维数组初始化**:讨论如何初始化多维数组。 - **指针与多维数组的区别**...

    C++程序设计课程 课件

    4.3.2则区分了数组指针和指针数组的概念,前者是指向整个数组的指针,后者是一组指针的集合。 4.4 指针与函数 4.4.1和4.4.2讨论了指针作为函数参数和函数返回值的情况。通过传递指针,函数可以直接修改调用者提供的...

    C&amp;C++经典常见面试题

    - **引用**:C++中的引用特性,其与指针的异同,以及在函数参数中的应用。 4. **并发编程**: - **线程**:线程的创建、同步和通信(互斥锁、条件变量、信号量)。 - **多进程**:进程间的通信(管道、共享内存...

    浅谈Delphi的指针

    本文旨在通过对比 C 语言中的指针使用方式,详细介绍 Delphi 中的指针使用技巧,并展示其与 C 语言在指针方面的异同。 #### 二、类型指针的定义 在 C 语言中,类型指针定义如下: ```c int *ptr; char *ptr; ``` ...

    指针&引用的课件(全)

    "C++大学基础教程第六章.ppt"可能涵盖了更广泛的C++基础知识,其中的第六章很可能深入讲解了指针和引用的概念,以及它们在实际编程中的应用,可能包括动态内存分配、数组指针、指针作为函数参数等主题。 通过这些...

Global site tag (gtag.js) - Google Analytics