C语言指针与数组的深度解析
立即解锁
发布时间: 2025-08-22 00:24:51 阅读量: 2 订阅数: 5 


C语言编程基础与实践
### C语言指针与数组的深度解析
#### 1. 指针基础
指针是C语言中非常重要的概念,它存储的是变量的内存地址。下面通过一些代码示例来理解指针的基本操作:
```c
int x = 1;
int y;
int z[10];
int *ip;
ip = &x; /* ip now points to x */
y = *ip; /* y is now 1 */
*ip = 0; /* x is now 0 */
ip = &z[0]; /* ip now points to z[0] */
```
在上述代码中,`int *ip;` 声明了一个指向整数的指针 `ip`。`&` 是取地址运算符,`*` 是解引用运算符。`ip = &x;` 让 `ip` 指向 `x` 的地址,`y = *ip;` 则是将 `ip` 所指向的值赋给 `y`,`*ip = 0;` 是将 `ip` 所指向的 `x` 的值修改为 0。
指针的声明语法与它可能出现的表达式语法相似。例如:
```c
double *dp, atof(char *);
```
这表明 `*dp` 和 `atof(s)` 的值都是 `double` 类型,并且 `atof` 的参数是一个指向 `char` 的指针。
指针通常被约束为指向特定类型的对象,但有一个例外,即 “指向 `void` 的指针”,它可以保存任何类型的指针,但不能直接解引用。
指针的运算也有一些规则:
- `*ip = *ip + 10;`:将 `ip` 所指向的值加 10。
- `y = *ip + 1;`:先取 `ip` 所指向的值,加 1 后赋给 `y`。
- `*ip += 1;`、`++*ip` 和 `(*ip)++`:都将 `ip` 所指向的值加 1。需要注意的是,`(*ip)++` 中的括号不能省略,因为 `*` 和 `++` 是右结合的,如果没有括号,会先对 `ip` 进行自增操作。
指针作为变量,也可以在不解引用的情况下使用。例如:
```c
int *iq;
iq = ip;
```
这会将 `ip` 的内容复制到 `iq` 中,使 `iq` 指向 `ip` 所指向的对象。
#### 2. 指针与函数参数
在C语言中,函数参数是按值传递的,这意味着被调用函数无法直接修改调用函数中的变量。例如,下面的 `swap` 函数无法交换调用函数中两个变量的值:
```c
void swap(int x, int y) /* WRONG */
{
int temp;
temp = x;
x = y;
y = temp;
}
```
要实现交换的效果,需要传递变量的指针:
```c
void swap(int *px, int *py) /* interchange *px and *py */
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
```
调用时使用 `swap(&a, &b);`,这样 `swap` 函数就可以通过指针间接访问和修改调用函数中的变量。
另一个例子是 `getint` 函数,它用于从输入中读取下一个整数:
```c
#include <ctype.h>
int getch(void);
void ungetch(int);
/* getint: get next integer from input into *pn */
int getint(int *pn)
{
int c, sign;
while (isspace(c = getch())) /* skip white space */
;
if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
ungetch(c); /* it is not a number */
return 0;
}
sign = (c == '-') ? -1 : 1;
if (c == '+' || c == '-')
c = getch();
for (*pn = 0; isdigit(c), c = getch())
*pn = 10 * *pn + (c - '0');
*pn *= sign;
if (c != EOF)
ungetch(c);
return c;
}
```
在这个函数中,`*pn` 被当作普通的 `int` 变量使用。`getch` 和 `ungetch` 函数用于处理多读取的字符。
#### 3. 指针与数组
在C语言中,指针和数组有着紧密的联系。任何可以通过数组下标实现的操作,也可以通过指针来完成。指针版本通常更快,但对于初学者来说可能更难理解。
例如,声明一个数组:
```c
int a[10];
```
这定义了一个包含 10 个整数的数组 `a`。如果声明一个指针:
```c
int *pa;
pa = &a[0];
```
那么 `pa` 就指向了数组 `a` 的第一个元素。此时,`*(pa + 1)` 就相当于 `a[1]`,`pa + i` 是 `a[i]` 的地址,`*(pa + i)` 是 `a[i]` 的值。
数组名实际上是数组首元素的地址,因此 `pa = a;` 与 `pa = &a[0];` 是等价的。而且,`a[i]` 等价于 `*(a + i)`,`&a[i]` 等价于 `a + i`。
需要注意的是,指针是变量,可以进行赋值和自增操作,如 `pa = a;` 和 `pa++;` 是合法的;但数组名不是变量,`a = pa;` 和 `a++;` 是非法的。
当数组名作为参数传递给函数时,传递的是数组首元素的地址。例如,下面是另一个版本的 `strlen` 函数:
```c
/* strlen: return length of string s */
int strlen(char *s)
{
int n;
for (n = 0; *s != '\0', s++)
n++;
return n;
}
```
在函数内部,`s` 是一个指针变量,`s++` 不会影响调用函数中的字符串,只是对 `strlen` 函数内部的指针副本进行操作。
函数定义中的参数 `char s[];` 和 `char *s;` 是等价的,但通常更倾向于使用 `char *s;`,因为它更明确地表明这是一个指针。
可以通过传递子数组的首地址来将部分数组传递给函数。例如:
```c
void f(int *arr) { ... }
int a[10];
f(&a[2]);
f(a + 2);
```
这两种方式都将数组 `a` 从第 3 个元素开始的子数组传递给了函数 `f`。
#### 4. 地址算术
指针的算术运算包括指针与整数的加减运算。如果 `p` 是指向数组元素的指针,`p++` 会使 `p` 指向下一个元素,`p += i` 会使 `p` 指向当前位置之后的第 `i` 个元素。
下面是一个简单的存储分配器的实现:
```c
#define ALLOCSIZE 10000 /* size of avail
```
0
0
复制全文
相关推荐









