C指针

来源:互联网 发布:新疆的网络受限 编辑:程序博客网 时间:2024/06/05 22:44

指针的定义及初始化

指针即为其值为内存单元地址的变量。指针声明:

int *ptr;

声明了一个int *类型,即为指向整数的int类型。
指针必须初始化后才可使用,可以在定义指针时进行,也可以通过赋值语句来完成。指针可以被初始化为NULL,0或一个地址。0是可以直接赋给指针变量的唯一整数值。NULL为一个符号常量,在stddef.h中定义,同时也包含在stdio.h等其他几个头文件中,值为NULL的指针表示其不指向任何对象。将指针初始化为0与初始化为NULL效果等价,但是初始化为NULL更好,因用0为指针赋值时,它首先被转换成一个适当类型的指针。

指针运算符

取地址运算符(address operator)&用于返回其操作数的地址值,取地址运算符的操作数必须是一个变量,不能应用于常量、表达式或者用存储类型register声明的变量。例:

int a = 10;int *ptr = &a;

间接寻址运算符(indirection operator)*,或称解引用运算符(dereferencing operator),返回其操作数(指针)指向的对象的值。必须正确初始化指针后才可以进行解引用操作。

int a = 10;int *ptr = &a;int b = *ptr; // b的值为10

%p转换说明符用于以十六进制整数形式输出一个内存地址。

int a = 1;printf("%p", &a); //输出a的地址

&与*运算符是互补的,不论以何种顺序作用于同一指针得到的结果均相同。

int a = 10;int *ptr = &a;printf("%p %p", *&ptr, &*ptr); //得到的结果相同

const与指针

const限定符用于告诉编译器这个被const限定的变量的值是不可被改写的。使用const声明的变量必须在定义时赋初始值(编译器不会报错但是可能引起无法预知的错误)。
对于指针有四种情况:指向非常量数据的非常量指针,指向非常量数据的常量指针,指向常量数据的非常量指针和指向常量数据的常量指针。其中,常量数据意味着数据不可被改写,常量指针以为指针类型变量的值不可被改写。
指向非常量数据的非常量指针:

int a = 9;int *ptr = &a;

ptr的值(指针所含地址)可以改变,ptr所指向的地址的值也可以改变。
指向非常量数据的常量指针:

int a = 9;int b = 10;int * const ptr = &a;ptr = &b; //error:ptr is const; cannot assign new address

ptr所指向的地址的值可以改变,但是ptr不可指向其他地址,即ptr的值不可改变。
指向常量的非常量指针:

int a = 9;const int *ptr = &a;*a = 1; //error

ptr所指向的地址可以改变,但是ptr所指向的地址的值不可以改变。
指向常量的常量指针:

int a = 9;const int * const ptr = &a;

ptr指向的地址和指向的地址的值均不能改变。

按引用向函数传递实参

向函数传递实参方式有两种,按值调用(call-by-value)和按引用调用(call-by-reference)。C语言中,所有实参都是按值传递的,可以使用指针和间接寻址运算符(*)来模拟按引用调用(simulating call-by-reference)。将一个变量地址传给函数时可以用间接寻址运算符来修改主调函数中对应内存单元的值。
指针其实也是按值传递,同样拷贝了一个指针类型变量并赋予相同的值,但是指针中保存着对应内存单元的地址,所以可以通过该地址来修改对应内存单元的值。
指针类型变量作为函数参数:

void swap(int *p1, int *p2);void swap(int *p1, int *p2) {    int temp = *p1;    *p1 = *p2;    *p2 = temp;}

使用时如下:

int a = 5, b = 6;swap(&a, &b);

sizeof运算符

sizeof运算符用于在编译期间计算出操作数的字节长度,返回以整数表示的操作数所占字节总数,因为是编译时执行,所以不会导致额外的运行时开销。
sizeof可应用于任何变量名、数据类型或数值(包括表达式的值)。当应用于一个变量名(不是数组名)或者常量时,其返回的是用于存储该变量所属特定数据类型或常量所需的内存字节数。
sizeof返回值类型为size_t,是一种由标准C定义的类型,是在stddef.h中定义的,同时也包含在stdlio.h等其他几个头文件中,相当于无符号整型unsigned int。
当sizeof的操作数是两个单词组成的数据类型名时,则需要圆括号将其括起来,若操作数是一个变量名或者一个一个单词组成的数据类型名时,那么可以省略圆括号。
当sizeof的操作数为指针时,返回的为指针类型变量所占的字节数。当sizeof的操作数为数组时,返回整个数组所占的字节数。例:

#include <stdio.h>size_t getSize(int *);int main() {    int array[20];    printf("%d %d", sizeof(array), getSize(array));    return 0;}size_t getSize(int *ptr) {    return sizeof(ptr);}

返回值并不同。
数组中元素个数也可用sizeof确定,如下:

double real[20];int number = sizeof(real) / sizeof(real[0]);

number即为数组中元素个数。

指针表达式和指针运算

指针只可以参与有限的几种算术运算。指针可以自增(++),可以自减(–),可以加上一个整数(+或+=),可以减去一个整数(-或-=),或者一个指针减去另一个指针。只有作用于数组时,指针的算术运算才是有意义的。
当给指针加上一个整数或者减去一个整数时,指针的增减值并非就是这个整数本身,而是这个整数乘以指针所指向对象的字节数,这个字节数取决于指向对象的数据类型。

int array[5] = {1, 2, 3, 4, 5};int *p = &array[1];p++; //p指向array[2]p += 2; //p指向array[4]p--; //p指向array[3]p -= 3; //p指向array[0]

对与指针相减操作,若两个指针指向同一数组,则返回的是两指针间(两指针指向的地址间)的数组元素个数:

int array[5] = {1, 2, 3, 4, 5};int *p1 = &array[1];int *p2 = &array[3];int x = p2 - p1; //x的值为2

仅在类型相同时一个指针才可以赋给另一个指针。而指向void的指针(即void * )是一个可以表示任何指针类型的通用指针,可以用其给任意类型的指针赋值,也可以用任意类型的指针给指向void的指针赋值,并且都无需使用强制类型转换运算符。
但是指向void类型的指针不能被解引用,指向void类型的指针简单包含一个位置类型数据的首地址,这个指针一次性将访问的字节数对于编译器而言未知,编译器必须知道数据类型才能确定解引用一个特定指针应该访问的字节数。
可以用相等运算符和关系运算符来对两个指针进行比较运算。指针比较运算比较存储在两个指针中的地址的大小,一般只有当两个指针均指向同一个数组的元素时才是有意义的。指针比较的一个常见用途是判断指针是否是NULL。

指针和数组

一个数组名可以看做一个常量指针。四种引用数组元素的方法等价:数组下标引用,将数组名作为指针的指针/偏移量引用,指针下标引用和基于指针的指针/偏移量引用。

int array[] = {1, 2, 3};int *p = array;//a,b,c,d的值均相等int a = array[2];int b = *(array + 2);int c = p[2];int d = *(p + 2);

指针数组

数组元素可以是指针,指针数组通常用于构造一个字符串数组。字符串数组每一个元素都是一个字符串,但是在C语言中,一个字符串实质上就是指向其第一个字符的一个指针。所以字符串数组的每个数据实际上就是指向字符串第一个字符的一个指针。声明如下:

const char *array[4] = {"hello", "are", "you", "????"};

注意,上述声明中数组每一个元素都是指向常量字符串第一个元素的指针,所以加上const以免被修改。这些字符串均以空字符结尾,所以长度要比字符数多一,长度分别为6,4,4,5。表面上看这些字符串放在数组中,但是实际上,数组中只存放指针 指向每个字符串的第一个字符。

函数指针

一个指向函数的指针包含的是一个函数在内存中的地址,函数名就是执行该函数功能的程序代码在内存中的起始地址。指向函数的指针既可以作为实参传递给函数, 也可以作为返回值从函数返回,还可以存入数组或者赋值给其他函数指针。
函数指针的声明如下:

int (*compare)(int a, int b);

以上声明了一个函数指针,形参分别为两个int值,返回值为int类型。括号为必须,若无括号则变成了返回指向int类型的指针的函数。
当函数的形参为指向函数的指针时,可以简写成:

void foo(int, int (*)(int, int));

即省略变量名。
使用函数指针如下:

(*compare)(1, 2);compare(2, 3); //不提倡该种方法,不能凸显出compare是一个函数指针而非函数

函数指针可以被赋值如下:

#include <stdio.h>int print(int);int main(int argc, char const *argv[]){    int (*func)(int) = print;    (*func)(2);    return 0;}int print(int a) {    printf("%d\n", a);    return a;}

函数指针数组的声明及使用如下:

#include <stdio.h>void func1(int);void func2(int);void func3(int);int main() {    void (*f[3])(int) = {func1, func2, func3};    (*f[0])(3);}void func1(int a) {    printf("%d\n", a + 1);}void func2(int a) {    printf("%d\n", a + 2);}void func3(int a) {    printf("%d\n", a + 3);}
0 0
原创粉丝点击