前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C/C++ 学习笔记四(指针、数组)

C/C++ 学习笔记四(指针、数组)

原创
作者头像
Celebi
修改2017-08-22 09:45:53
2.3K0
修改2017-08-22 09:45:53
举报
文章被收录于专栏:Celebi的专栏Celebi的专栏

指针

什么是指针?

指针其实是内存单元地址

什么是指针变量?

指针变量是用于存储内存单元地址的变量

指针变量存储的实质

顾名思义,指针变量存储的是内存单元的值,即存储的值其实指向的一个特定类型内存区域的起始地址。

如下例子,整型变量a的值为123,其内存单元地址为0x104,指针变量存储的值是变量a的内存单元0x104。

指针变量指向了内存地址起始为0x104,长度为4个字节的内存单元。p也可以对这个4个单元进行操作。

代码语言:javascript
复制
int a = 123;
int *p = &a;

指针变量的运算

c语言中,我们可以像普通整型数据一样,对指针变量进行运算。

指针变量的转换

普通类型的指针变量都可以直接赋值void *空类型指针, 但空类型指针需要强转才可以转成普通类型指针。

void 指针是一种特殊的指针,表示“无类型的指针”,因为其没有指定类型,所有它可以指向任何类型的数据,也就是说任何类型的指针可以直接复制给void指针。

代码语言:javascript
复制
void * vp;
int * ip;
...
vp = ip;

但是void指针赋值给其他类型的指针时,必须强制转换

代码语言:javascript
复制
void *vp;
int * ip;
...
ip = (void *)vp;

这是因为指定了类型的指针变量指向了内存的一块区域,但空类型指针无法确定指向内存区域的大小。

需要注意的是,不同类型指针变量相互转换时,需避免访问非法的内存区域,导致程序异常退出。 如下例子,chP指向了一段长度为1字节的变量a的内存区域,当其强制转换成int指针时,则超出了编译器分配的内存区域,程序会异常退出。

代码语言:javascript
复制
    char  a = 'a';
    char * chP = &a;
    int * iP = (int *)chP;
    *iP = 123;

指针变量的算术运算

指针变量可以使用算术运算符实现自增减,如下例

代码语言:javascript
复制
    int a = 123;
    int * ip = &a;
    printf("p = %p \n", ip);

    char * charPtr = (char *)ip;
    double * doublePtr = (double *)ip;
    charPtr++;
    doublePtr++;
    printf("charPtr = %p \n", charPtr);
    printf("doublePtr = %p \n", doublePtr);

假设&a地址为0x100,则上面例子的输出为

代码语言:javascript
复制
p = 0x100
charPtr = 0x101
doublePtr = 0x108

即使指针变量的算术运算为增减sizeof(数据类型)的大小。上例子中p的值为0x100,强转为char 后自增1,结果为0x101,强转为double 后自增1为,0x108(0x100+0x8)。

同理因为空指针类型无法得知其指向区域的长度,void *指针便无法进行增减操作。

数组

C语言中,数组与指针是一种非常暧昧的关系,因数组和指针经常可以相互的转换,所以经常会将其两者混淆。 真正的事实是,两者拥有不同的存储结构,但引指针的灵活性,两者可以相互的引用、转换。

数组的存储结构

与指针的存储结构相比,数组在内存中占据的是连续的字节单元。即指针存储的长度根据计算机不同,是一个固定的大小 (32位4个字节、64位8个字节),数组存储的是一块连续的内存区域。

代码语言:javascript
复制
    int * p =  ....;
    int a[3] = { 1,2,3 };
    printf("sizeof(p) :%d \n",sizeof(p));   //sizeof(p) :4
    printf("sizeof(a) :%d \n", sizeof(a));  //sizeof(p) :12

为什么指针可以访问数组的元素?

那为什么指针可以访问数组中元素?

这是因为数组标识符表示的是该数组的第一个元素的地址,当指针指向数组标识符时,便可以通过指针访问数组中的各个元素。

代码语言:javascript
复制
    int a[3] = {1,2,3};
    int * p = a;

    printf("a :%p \n",a);
    printf("* p  :%p \n", p);
    printf("* a[1]  :%p \n", *(p++)); //1

函数调用时数组作为参数时为地址传递

C语言的标准中规定:所有的数组在作为参数传递时,都转换成指向数组起始地址的指针,其他参数均采用值传递。

采用地址传递好处是形参不存在存储空间,编译系统不为形参分配内存,数组名或者指针便是一组连续空间的首地址。

例如下面例子输出的size都为4。

代码语言:javascript
复制
void fp(char *arr)
{
    printf("fp size : %d", sizeof(arr));
}

void fa(char arr[])
{
    printf("fa size : %d", sizeof(arr));
}
int main()
{
    char c[] = "Hello";
    fp(c);  // fp size :4
    fa(c);  // fa size :4
}

数组与指针的区别

数组和指针其实并不是一个相同概念,虽然在日常的使用中,经常可以使用指针代替数组,用于遍历数组的元素,例如

代码语言:javascript
复制
char array[6]="hello";
char *chPtr = array;
char charE = chPtr[1];
printf("%c \n",charE);

再来一个例子,可以证明数组并不是指针:

代码语言:javascript
复制
/* 文件 f1.c*/
int a[3] = {1,2,3};
/* 文件 main.c */
#include "f1.c"
int main()
{
    extern int * a;
    return 0;
}

执行上面代码会提示“a”: 重定义;不同的间接寻址类型 在使用extern int a时,编译器认为a是一个在外部声明的整型指针变量,但f1.c中,a是一个长度为3的整型数组,在32位系统系统下,int 长度为4字节,而int [3]长度为4*3 = 12字节。

再看一个例子

代码语言:javascript
复制
    char * a = "hello";
    char arr[6] = "hello";
    if (a == arr)
    {
        printf("same");
    }
    else {
        printf("different");
    }

程序执行后打印different。这是因为a指向的是存储于数据段静态变量hello,arr则指向编译器分配的内存空间。两者的值并不一样。

对于数组而言,编译器已经为数组分配了一定的空间以及对应的地址,通过数组地址的偏移,可以访问该数组的元素。 而指针,编译器为其分配了空间,用于存储地址值。而对于存放在其中的值,只有在程序运行时才能知道。

使用指针和数组的注意点与建议

1. 使用指针前必须初始化,否则会指向错误的内存区域,导致程序异常。

2.使用NULL指针作为函数调用失败的返回值。类似malloc函数,当分配内存失败时返回NULL,用以表示为一种异常情况。

3.在不知内存区域具体类型情况下,避免对void 指针进行算术操作

例如,下例子对p任何的操作都会导致程序出错

代码语言:javascript
复制
void *p;
p++;
p--;

4. 不同类型指针转换时,注意不超出编译器分配的内存区域。

如下例子,ip使用了超出了编译器分配的内存,会导致程序异常退出

代码语言:javascript
复制
char a = '2';
char * cp = &a;
int *ip = (int *)cp;
*ip = 123;

5.避免指针和整型数据的直接转换。

总结

1.指针变量是变量,存储内存地址的变量。 3.数组存储的是一段连续的内存区域 4.数组标识符存储了,一段内存区域的起始地址 5.数组作为参数传递时是地址传递,其他类型则为值传递

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 指针
  • 指针变量的运算
  • 数组
  • 为什么指针可以访问数组的元素?
  • 数组与指针的区别
  • 使用指针和数组的注意点与建议
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档