指针与数组
指针与地址
通常的机器有一系列连续的编号或编址的存储单元,它们可以以连续的方式存放不同数据类型的数据。如4个相邻的字节存放一个long类型变量,单个字节存储char类型变量。
一元运算符&可用于取一个地址。
P = &c;
将c的地址复制给变量P,我们称P为指向c的指针。地址运算符&只能应用于内存中的对象,即变量和数组。 他不能作用于表达式,常量或寄存器变量。
一元运算符*是间接寻址或间接引用的运算符。当它作用于指针时,将访问指针所指向的对象。
int x = 1, y = 2, z[10];
int *ip; //声明ip为int类型的指针
ip = &x; //ip指向x
y = *ip; //y == 1
*ip = 0; //x为0
ip = &z[0]; //指向z[0]
指针只能指向某种特定类型的变量,每个指针都必须指向某种特定的数据类型。(EXCEPTION:void类型的指针可以存放指向任何数据类型的指针,但是不能间接引用自身。)
一元运算符的*和&的优先级高于算术运算符,*ip += 1等同于 ++*p等同于(*ip)++。
指针和函数
指针参数使得被调用的函数可以访问和修改主调函数中对象的值。除了经典的swap例子外,还有:
函数getline接受自由格式的输入,并且执行转换,输入的字符分解成整数,每次调用获得一个整数。getint需要返回转换后得到的整数,在到达输入结尾时要返回文件结束标记。
scanf的设计方法:将标识是否到达文件结尾状态作为getint的返回值,使用一个指针参数储存转换后的整数并传回主调函数。
int n, array[SIZE], getint(int *);
for (n = 0; n < SIZE && getint(&array[n]) != EOF; n++)
;
此getint函数在到达文件末尾是返回EOF,当下一个输入不是数字时返回0,当输入中包含一个有意义的数字时返回一个正值。
#include <ctype.h>
int getch(void);
void ungetch(int);
//输入中的下一个整数赋值给*pn
int getint(int *pn){
int c, sign;
while (isspace(c = getch())) //跳过空白符
;
if(!isdigit(c) && c != EOF && c != '+' && 'c' != '-') {
ungetch(c); //输入不是数字
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;
}
*getchar(), getc(), getch(), getche()
The difference between getc() and getchar() is getc() can read from any input stream, but getchar() reads from standard input. So getchar() is equivalent to getc(stdin).
getch() is a nonstandard function and is present in conio.h header file which is mostly used by MS-DOS compilers like Turbo C. It is not part of the C standard library or ISO C, nor is it defined by POSIX (Source: http://en.wikipedia.org/wiki/Conio.h)
Like above functions, it reads also a single character from keyboard. But it does not use any buffer, so the entered character is immediately returned without waiting for the enter key.
getche() like getch(), this is also a non-standard function present in conio.h. It reads a single character from the keyboard and displays immediately on output screen without waiting for enter key.
数组和指针
数组下标能完成的事情都可以通过指针来实现,执行速度也更快。
int *pa, a[10];
pa = &a[0];
*(pa + 1)指向a[1], (pa + i)指向a[i]。然而在计算数组元素a[i]时C语言会先把其转化成(a+i)的形式,然后再进行求值。
同理,加上取址运算符,&a[i]和a+i的含义也是相同的。
把数组名传递进一个函数时,实际上传递的是该数组的第一个元素的地址。在被调用函数中,该参数是一个局部变量,因此,数组名必须是一个指针,也就是一个存储地址值的变量。
例如,编写strlen函数:
int strlen(char *s){
int n;
for (n = 0; *s != '\0'; s++)
n++;
return n;
}
在函数定义中,形式参数(formal parameters)
char [] 和 char *s 是等价的。我们更习惯于最后一种方式,比前者更直观的表明了参数是一个指针。如果将函数名传递给函数,函数可以根据情况判断是按照数组还是指针处理,随后根据相应方式操作该参数。
也可以把指向子数组的起始位置的指针传递给函数,这样就将数组的一部分传递给了函数。例如,如果a是一个数组,那么下面两个函数调用f(&[2])与f(a+2)都将把起始于2的子数组地址传递给函数f。在函数f中,参数的声明可以是f(array[])和f(*array)。
有效的指针变量的运算包括:
- 相同类型的指针之间的赋值运算
- 指针同整数之间的加法运算或减法运算
- 指向相同数组中元素两个指针的比较运算
- 指针赋值为0或指针与0之间的比较运算
其他的运算都是非法的。
字符指针
字符串常量是一个字符数组。字符串出现在程序中时,一般是通过字符指针访问该字符串的。字符串可以通过指向第一个元素的指针访问。
除了作为函数参数之外,字符串常量还有其他用法。
char *s
s = " hello world! "
指向该字符数组的指针被赋值给s,该过程并没有字符串的赋值,而只是涉及到指针的操作。因此,以下两个定义有很大差别:
char s1[] = "hello world!"
char *s2 = "hello world!"
第一个是仅足以存放字符串以及空字符的一维数组。数组中的单个字符可以修改,但是s1始终指向同一个储存位置。而s2可以指向其他地址,如果试图修改字符串的内容,结果是没有定义的。