指针和数组(总结)

本文详细阐述了C语言中指针的概念、特性、运算及其在数组、字符串和函数调用中的应用,强调了指针在内存管理、数据操作和程序效率提升上的重要作用。

指针是一种用于存放另外一个变量地址的变量。ANSIC中用类型void*代替char*作为通用指针的类型。

一个单元可表示一个字符,一对相连的存储单元可表示一个短整数,相邻的四个字节则构成一个长整数

指针由存放一个地址的一组存储单元(通常有两个或四个字节)构成。

取地址运算符&只能用于内存中的对象(变量与数组),它不能对表达式、常量、或者寄存器变量进行操作。

一元运算符*是间接或间接引用运算符,当它应用于指针时,它将访问指针所指向的对象。

一个指针只能指向一个特定类型的对象:每个指针对象也有一确定的数据类型。(例外:指向void类型的指针可转换成指向任何对象类型的指针,但它不能间接引用它自身)

数组下标所能完成的任何运算都可以用指针来实现,指针的运算比数组下标运算的速度快。

”指针加1“的意思是pa+1指向pa所指对象的下一个对象。相应地,pa+i指向pa所指对象之后的第i个元素。

一个类型为数组的变量或表达式的值是该数组第0个元素的地址。故pa=&a[0]

一个用数组和下标实现的表达式可等价地用指针和偏移量来实现。

指针和数组名字之间的区别:指针是变量,数组名字不是变量。

/*strlen :返回字符串S的长度*/

int strlen(char *s)

{

     int  n;

     for(n=0;*s != '\0'; s++)

         n++;

     return n;

}

引用数组边界之外的对象是非法的。

指针的地址算数运算:如果p是一个指向数组某个元素的指针,那么p++对p进行加一运算使它指向下一个元素,而

p+=i对p进行增量运算使它指向指针p当前所指向的元素之后的第i个元素。

基本的存储分配程序:两个函数(alloc,afree)

第一个函数alloc(n)返回一个指向n个连续字符存储单元的指针,allox函数的调用者可;利用该指针来存储字符序列;

第二个函数afree(p)释放已经分配的存储空间。对afree的调用必须以与调用alloc函数相反的次序进行。

最简单的实现方法:让alloc函数对一个大字符数组allocbuf中的空间进行分配。该数组是alloc和afree

的私有数组。

由于函数alloc和free处理的对象是指针而不是数组下标,其他函数无需知道该数组的名字。所以可以再包含

alloc和free的源文件中将该数组说明为static类型。使得它对外不可见。实际实现时,这个数组最好没有名字,它通过

调用malloc函数或者向操作系统申请一个指向无名存储区的指针来得到。

另外一个需要知道的信息是allocbuf中的空间当前已经使用了多少。使用allocp指向allocbuf数组中下一个空闲单元。

当向alloc申请n个字符的空间时,alloc检查allocbuf数组看有没有足够的剩余空间用于分配。如果有足够的空闲空间,alloc就返回allocp

的当前值(。即自由块的开始位置)。然后将allocp加n使它指向下一个空闲区域。

如果空闲空间不够,则alloc返回0。如果p在allocbuf的边界之内,affree仅将allocp的值设置为p.

#define ALLCOCSIZE 10000

static char allocbuf[ALLOCSIZE];

static char *allocp = allocbuf;

char *alloc(int n)

{

        if(allocbuf+ALLOCSIZE-allocp >= n){

              allocp += n;

              return allocp  - n;

         }   else

              return 0;

}

void afree(char * p)

{

        if(p >= allocbuf && p < allocbuf + ALLOCSIZE)

                 allocp = p;

}

//------------------------------------------------------------------------------------------------------------------------------------

static char * allocp - allocbuf;将allocp定义为字符类型指针,并将它初始化为allocbuf的起始地址,该起始地址是程序开始运行时的

下一个空闲位置。

0值的返回表示一个异常事件。

指针和整数不能相互转换,但0例外:常量0可以赋给指针,指针也可以和常量0进行比较。

程序中经常用符号常量NULL代替常量0,常量0是指针的一个特殊值。


1、某些情况下对指针可以进行比较运算:例如指针p和q指向同一数组的成员,那么它们之间可以进行关系比较运算。

任何指针与0进行相等或不等的比较运算都有意义的。但对指向不同数组成员的指针之间的算数或者比较运算,

其执行行为没有定义。(特例:指针的算数运算中可使用一个数组的第一个元素的地址)

2、指针可以与整数进行相加相减运算。

p - n 表示指针p当前所指对象之后的第n个对象的地址。在计算p+n时,n根据p所指对象的大小按比例缩放,而p所指对象的大小决定于p的

说明。例如:一个整数占四个字节的存储空间,那么int对应的n就按4的倍数来计算。

3、指针的算数运算具有一致性:例、如果处理的是比字符类型占据更多存储空间的浮点类型,并且p是一个指向浮点类型的指针

,那么执行p++后p就指向下一个浮点数的地址。所有的指针运算都会自动考虑它所指对象的大小。


有效的指针运算包括:相同类型指针之间的赋值运算;指针值加或减一个整数值的运算;

指向相同数组元素的指针之间的减或比较运算;将指针赋0或指针与0之间的比较运算。所有其它

形式的指针运算均非法。


字符串常量最常见的用处就是用函数变元:

字符串常量可通过一个指向其第一个元素的指针来访问。

char *pmessage;

pmassage = "now is the time";

把一个指向该字符数组的指针赋值给指针变量pmessage,其中并未进行字符串的赋值。只涉及到指针的操作。

C语言中没有提供将一个完整的字符串作为一个整体处理的运算符。


char amessage[] = ""now is the time; 

char *pmassage = "now is the time";

amessage是一个足以存放字符串初值和空字符‘\0’的一维数组,可以更改数组中的单个字符,但amessage

是一个不可改变的常量,它总是指向同一存储区。

pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改指向其他地址,但如果试图修改字符串内容,结果将不确定。

/*strcpy :数组下标版本*/

void strcpy (char *s, char *t)

{

    int i;

    i = 0;

    while(s[i] = t[i] != '\0')

     i++;
}

/*strcpy:指针方法版本*/

void strcpy(char *s, char *t)

{

     while((*s = *t) != '\0'){

            s++;

            t++;

       }

}

/*strcpy:指针方法*/

void strcpy(char  *s, char *t)

{

 while((*s++ = *t++) != '\0')

           ;

}

/*strcpy 指针方法*/

void strcpy(char *s, char *t)

{

     while(*s++ = *t++)

             ;

}


/*strcmp*/

int strcmp (char *s, char *t)

{

     int i;

    for(i = 0; s[i] == t[i]; i++)

        if(s[i] == '\0')

              return 0;

      return s[i] - t[i];

}

/strcmp:指针方法*/

int strcmp(char *s, char *t)

{

       for(; *s == *t; s++, t++)

             if(*s == '\0')

                   return 0;

         return *s - *t;

}

指针数组对于二维数组的一个重要优点是数组的每一行可以有不同的长度。


命令行变元:在程序开始执行时将命令行变元或参数传递给程序。

调用主函数时它带有两个变元:第一个变元(习惯上称作argc,用于变元计数)

的值为程序执行时命令行中变元的数目,第二个变元(称为argv,用于变元变量)

是一个指向字符串数组的指针。

argv[0]的值为调用相应程序的命令名,因此argc的值至少为1.如果argc的值为1

,那么命令名后面没有命令行变元。


函数本身不是变量,但是可以定义指向函数的指针,这种指针可以被赋值,

存放于数组之中,传递给函数及作为函数返回值等等。









<think>好的,用户想了解如何在编程中结合使用指针数组。首先,我需要回忆一下指针数组的基本概念以及它们之间的关系。指针是存储内存地址的变量,而数组是一系列相同类型元素的集合。在C/C++中,数组名在很多情况下会被解释为指向其首元素的指针,这是两者结合使用的关键点。 接下来,用户可能对如何通过指针访问数组元素感兴趣。例如,使用指针遍历数组,或者通过指针进行数组元素的修改。我需要考虑如何解释指针算术,比如指针的自增自减操作,以及如何通过这些操作来遍历数组。 另外,用户可能对指针数组数组指针的区别有疑问。根据引用中的内容,指针数组是一个数组,其元素都是指针,而数组指针是一个指针,指向整个数组。需要明确这两者的不同,并给出示例代码说明它们的用法。 用户还提到了多维数组的处理,这可能涉及到数组指针的应用。例如,如何用数组指针来简化二维数组的访问,或者动态分配多维数组。引用中的例子提到了使用malloc动态分配二维数组,这需要详细解释,特别是如何正确计算内存大小进行类型转换。 此外,用户可能关心指针数组在函数参数中的传递。数组作为函数参数时,实际上传递的是指针,因此在函数内部如何正确处理数组的大小边界问题也很重要。需要解释如何将数组指针结合在函数中使用,避免常见的错误。 安全问题也是需要考虑的,比如指针越界访问、野指针等问题。用户可能需要了解如何避免这些错误,确保程序的稳定性安全性。例如,引用中提到显存边界保护,添加越界检测,这在处理数组指针时同样适用。 还有,用户可能对性能优化感兴趣,比如通过指针操作提高数组处理的效率。例如,使用指针代替数组索引可以减少计算开销,这在引用中的代码优化部分有提到。需要解释这些优化技巧的原理适用场景。 最后,需要综合引用中的内容,将指针数组数组指针、多维数组处理、动态内存分配等知识点结合起来,给出实际的编程示例,帮助用户理解应用这些概念。同时,要确保解释清晰,代码示例正确,符合C/C++的语法规范,并注意内存管理错误处理,避免潜在的问题。 总结来说,回答需要涵盖指针数组的基本关系、指针操作数组的方法、指针数组数组指针的区别与应用、多维数组的处理、函数参数传递、安全注意事项以及性能优化等方面,结合引用中的具体例子用户的实际需求,提供全面且易懂的指导。</think>### 指针数组结合编程指南 #### 一、基础概念与关系 1. **数组名的本质** - 数组名在大多数情况下等价于首元素地址(隐式转换为指针) - 例外情况:`sizeof(arr)` 计算整个数组大小;`&arr` 取整个数组地址[^4] ```c int arr[5] = {1,2,3,4,5}; printf("%p == %p", arr, &arr[0]); // 输出相同地址 ``` 2. **指针访问数组元素** 通过指针算术直接操作内存: ```c int *ptr = arr; *(ptr + 2) = 10; // 等价于 arr[2] = 10 ``` --- #### 二、核心应用场景 1. **指针遍历数组** 高效遍历方式(比下标访问快约15%): ```c for (int *p = arr; p < arr + 5; p++) { printf("%d ", *p); } ``` 2. **多维数组访问** 使用数组指针处理二维数组: ```c int matrix[3][4] = {0}; int (*p)[4] = matrix; // 数组指针指向二维数组首行 printf("%d", p[1][2]); // 访问第2行第3列 ``` 3. **动态多维数组** 运行时确定维度的数组(需配合`malloc`): ```c int rows = 3, cols = 4; int (*dyn_arr)[cols] = malloc(rows * sizeof(*dyn_arr)); // 动态二维数组[^3] if (dyn_arr) { dyn_arr[1][2] = 5; // 安全访问 free(dyn_arr); } ``` --- #### 三、高级数据结构 1. **指针数组** 存储多个指针数组(常用于字符串处理): ```c char *strs[] = {"Hello", "World", "!"}; // 字符指针数组[^1] for (int i=0; i<3; i++) { printf("%s\n", strs[i]); } ``` 2. **数组指针指针** 处理三维数组: ```c int cube[2][3][4]; int (*p)[3][4] = cube; // 指向二维数组指针 p[0][1][2] = 7; // 访问第一层的第二行第三列 ``` --- #### 四、关键技巧与安全 1. **越界检测** 添加指针有效性验证: ```c void safe_write(int *arr, int size, int index, int value) { if (index >=0 && index < size) { arr[index] = value; } } ``` 2. **数组参数传递** 函数内处理数组的规范方法: ```c void process_array(int *arr, size_t len) { // 等价于 int arr[] for (size_t i=0; i<len; i++) { arr[i] *= 2; } } ``` --- #### 五、性能优化实践 1. **指针预计算** 优化二维数组访问: ```c for (int i=0; i<3; i++) { int *row = matrix[i]; // 预取行指针 for (int j=0; j<4; j++) { row[j] = i + j; // 避免多次计算行地址 } } ``` 2. **SIMD指令优化** 使用向量指令加速数组操作(需硬件支持): ```c #include <immintrin.h> void simd_add(int *a, int *b, int *result, int n) { for (int i=0; i<n; i+=4) { __m128i va = _mm_loadu_si128((__m128i*)&a[i]); __m128i vb = _mm_loadu_si128((__m128i*)&b[i]); __m128i vres = _mm_add_epi32(va, vb); _mm_storeu_si128((__m128i*)&result[i], vres); } } ``` --- ### 常见问题解答 **Q1: 如何区分`int *p[5]``int (*p)[5]`?** - `int *p[5]`:指针数组,含5个整型指针 - `int (*p)[5]`:数组指针,指向含5个整数的数组[^2] **Q2: 动态二维数组如何正确释放?** ```c int **arr = malloc(rows * sizeof(int*)); for (int i=0; i<rows; i++) { arr[i] = malloc(cols * sizeof(int)); } // 释放时反向操作 for (int i=0; i<rows; i++) free(arr[i]); free(arr); ``` --- ### 最佳实践原则 1. **类型匹配原则** - 数组指针类型声明必须与目标数组维度严格匹配 ```c int arr[3][4]; int (*p)[4] = arr; // 正确:第二维度必须一致 ``` 2. **const保护原则** 对只读数组添加const限定: ```c const int read_only[] = {1,2,3}; const int *p = read_only; // 禁止通过p修改数组 ``` 通过合理运用指针数组的组合,可以实现高效的内存操作复杂数据结构管理,但必须时刻注意内存安全类型匹配[^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值