首页 > 其它语言 > C语言指针理解

C语言指针理解

指针

 

什么是指针?

 

 

简单的来讲:指针就是一个变量,它跟其他类型的变量是一样的概念,只不过它储存的是内存地址(内存单元编号)。

 

比如:我们的身份证就是一个指针变量,它所存储的是我们的家庭住址。

我们的住址,还有房间的编号,门牌号,它们都是指针常量。

 

假如有个同学借你的身份证去上网回来,你不在宿舍,他就把身份证夹在了放在书架的书里。然后写了张纸条放在桌子上,就去玩了,你回来看到纸条你就知道身份证放在哪里了。

(纸条就是一个指针变量,它所存储是你身份证的地址。)

这时又来了个同学向你借书,你叫他自己找,在书架上的“第三层第一格里”(这就是一个指针常量)

 

当理解指针的概念时,就有这样的思想了,指针就是地址,地址就是指针。

但是要搞清楚的是,指针是指针,指针变量是指针变量,刚开始就是这里,

搞糊涂我了,我以为指针就是指针变量,那么指针变量就是地址了。

 

指针的声明

数据类型 指针操作符(*) 变量

 

int *p;//声明一个储存int型变量地址的指针

char *cp;//声明一个存储char型变量地址的指针

 

书上说的指向int型的指针,其实就是存储int型变量地址的指针。

这些都是我片面的理解,错了请喷,但是喷了请告诉我为什么??谢谢!

 

指针的使用

指针变量的赋值和初始化

 

为指针变量赋值就是设定指针所指向地址的过程

int i = 10;//声明一个int型变量,并初始化为10

int *p;//声明一个指向int型的指针变量

p = &i;//使p指向i,&i是取i的地址。

 

int i = 8;//声明一个int型变量,并初始化为8

int *p = &i;//声明一个指向int型的指针变量,并初始化指向i

 

注意指针的初始化,当我们不为指针变量初始化时,我们应该为其赋值为NULL == 0

int *p = NULL;//使p指向空。就可以避免造成一些不必要的错误。

 

指针的一些简单应用

 

①测试指针类型的字节长度。

#include <stdio.h>

 

int main(void)

{

int *ip = NULL;

char *cp = NULL;

float *fp = NULL;

double *dp = NULL;

long *lp = NULL;

long long *llp = NULL;

printf("sizeof(int*) = %d\n",sizeof(ip));

printf("sizeof(char*) = %d\n",sizeof(cp));

printf("sizeof(float*) = %d\n",sizeof(fp));

printf("sizeof(double*) = %d\n",sizeof(dp));

printf("sizeof(long *) = %d\n",sizeof(lp));

printf("sizeof(long long *) = %d\n",sizeof(llp));

return 0;

}

可以得出所有类型指针的字节长度都为4个字节。

 

②指针如何访问和修改所指的变量。

#include <stdio.h>

 

int main(void)

{

int i = 10;

int *p = &i;

printf("通过*p访问i\n");

printf("i = %d\n",i);//输出i的值

printf("*p = %d\n",*p);//输出p所指地址的内容(就是值)

/* *p是取指针变量所指地址的内容 */

printf("\n通过*p 修改 i的值\n");

*p = 3;

printf("i = %d\n",i);

printf("*p = %d\n",*p);

return 0;

}

可以得出在此代码中 *p = = i    i = = *p

 

 

③多次赋值指针变量的变化。

#include <stdio.h>

 

int main(void)

{

int x = 8;

int y = 6;

int *p = NULL;

printf("&x = %p\n",&x);//取x的地址

printf("&y = %p\n",&y);//取y的地址

printf("&p = %p\n",&p);//取p本身的地址

printf("&*p = %p\n",&*p);//取p所指的地址

p = &x;

printf("p = &x\n");

printf("&x = %p\n",&x);

printf("&p = %p\n",&p);

printf("&*p = %p\n",&*p);

p = &y;

printf("p = &y\n");

printf("&y = %p\n",&y);

printf("&p = %p\n",&p);

printf("&*p = %p\n",&*p);

return 0;

}

就像其他类型的变量,每一个赋值就是改变他的值。

const指针

 

① 指向const的指针变量

声明方式:

int const *p;

const int *p;

 

就是指向const型变量的指针变量

#include <stdio.h>

 

int main(void)

{

const int a = 8;

const int b = 10;

const int *p;//其声明方式

p = &a;

printf("*p = %d\n",*p);

p = &b;

printf("*p = %d\n",*p);

//*p = 10;错误不可通过*p修改b的值

return 0;

}

可以重新为p赋值,但是不可以通过指针修改变量的值

 

② const 型指针变量,声明方式如下:

int *const p;

声明一个const型指针变量,赋值后就不可以,再为其赋值了。如同其他类型的变量一样。

如:

const int i = 10;

i = 8;//这是错误的

 

 

#include<stdio.h>

 

int main(void)

{

int i = 8;

nt j = 10;

int *const p = &i;

printf("i = %d\n",i);

printf("*p = %d\n",*p);

//p = &j;//错误

return 0;

}

 

③ 指向const的const指针变量,声明方式如下:

const int *const p;

 

声明一个指向const型变量的const型指针变量。

 

不可通过指针修改变量的值。

不可在为其赋值。

 

#include <stdio.h>

 

int main(void)

{

int i = 6;

const int *const p = &i;

printf("i = %d\n",i);

printf("*p = %d\n",*p);

return 0;

}

 

int i = 8;

const int *const p;

p = &i;//这是错误的。

*p = 10;//不可通过*p改变其值

 

指针与函数

 

指针形参

指针作为形参实现交换两个数。

#include <stdio.h>

 

void  exch(int *x,int *y)

{

int t = 0;

t = *x;

*x = *y;

*y = t;

}

 

int main(void)

{

int i = 10;

int j = 11;

exch(&i,&j);

printf("i = %d\n",i);

printf("j = %d\n",j);

return 0;

}

/*****************************************

输出结果

i = 11

j = 10

请按任意键继续. . .

******************************************/

成功交换两个数,如果不用指针作为形参可以实现吗?

#include <stdio.h>

 

void  exch(int x,int y)

{

int t = 0;

t = x;

x = y;

y = t;

}

 

int main(void)

{

int i = 10;

int j = 11;

exch(i,j);

printf("i = %d\n",i);

printf("j = %d\n",j);

return 0;

}

交换失败,这是为什么?(函数传递 是把实参赋值给形参。)

 

因为这个函数只是把实参的值传递给形参,相等于:

x = i,y = j;

形参和实参不是同一个地址,是互不相干的,都有自己的家(内存地址)

但是指针作为形参时就不一样了,因为实参传递给形参是地址,那函数内部就可以直接访问内存了。

 

 

指针型函数

函数是形参可以为指针型,那函数的返回值可以为指针型吗?

答案是可以的,指针型函数可以实现。

其声明方式如下:

数据类型 *函数名(形参列表)

int *a(void);

 

编写一个函数返回最大数的地址。

 

#include <stdio.h>

 

int *p_max(int *p,int *p1)

{

if(*p > *p1)

return p;

else

return p1;

}

int main(void)

{

int x,y;

int *pt;

printf("请输入两个整数:");

scanf("%d %d",&x,&y);

pt = p_max(&x,&y);

printf("*pt = %d\n",*pt);

return 0;

}

 

函数型指针

函数定义的时候实际上就是定义了一个函数变量,那么是否可以将这个变量的地址赋值给指针呢?当然了,可以使用函数型指针实现。

函数型指针就是指向函数的指针变量。

其声明方式如下:

int f(int x,int y);

 

int (*p)(int,int);

 

函数型指针的赋值和调用

#include <stdio.h>

 

int add(int x,int y)

{

return x + y;

}

int main(void)

{

int t = 0;

int (*p)(int,int);

p = add;

t = (*p)(8,8);

printf("t = %d\n",t);

printf("%d\n",p(8,8));

return 0;

}

赋值

如:

int (*p)(int,int);

p = 函数名;

 

int (*p)(int,int) = 函数名;

 

调用有两种形式:

① (*p)(形参列表);

② p(形参列表);

 

注意:要保证左值和右值一样,如

int add(int x,int y);

int amx(int x);

int (*p)(int,int) = add;

p = amx;//这是错误,因为他们不匹配

 

void型指针

 

书上关于void型指针一页都没有,为什么介绍的这么少。

void型指针有什么用?用在哪里?

 

当你不知道这个指针变量要指向什么类型的地址时,就可以使用void指针了。

 

#include <stdio.h>

 

int main(void)

{

void *vp;

int i = 10;

char c = '8';

float f = 9.3;

int *p;

printf("vp = &i\n");

vp = &i;

//printf("*vp = %d\n",*vp);

p = vp;

printf("*p = %d\n",*p);

printf("vp = &c\n");

vp = &c;

p = vp;

printf("*p = %c\n",*p);

printf("vp = &f\n");

vp = &f;

p = vp;

printf("*p = %f\n",*p);

return 0;

}

 

指针与数组

 

指向数组元素的指针

指向数组元素的指针变量,其指向的地址属于一个数组中的某一个元素。

如:

#include <stdio.h>

 

int main(void)

{

int array[10] = {1};

int *p = &array[0];

int *p1 = array[0];

int *p2 = &array;

int *p3 = array;

printf("&array[0] = %p\n",&array[0]);

printf("&*p = %p\n",&*p);

printf("&*p1 = %p\n",&*p1);

printf("&*p2 = %p\n",&*p2);

printf("&*p3 = %p\n",&*p3);

return 0;

}

我想的是这些输出都是相同的,但是为什么&*p1(array[0])不同???

因为 p1 指向的是 array[0]

而array[0] == 1; 所以 p1 = 1; p1所指向的是内存中 1单元。

注意:虽然指针变量可以赋值为整数,但是不用这样做,很容易出现错误。

假如我们随便为指针变量赋值了个整数,而这个地址有程序中变量占用了。

那不是很危险,严重会导致那个程序崩溃。万一那个地址是系统占用的呢?

 

指针访问数组

数组不仅可以通过下标访问,还可以使用指针变量来操作数组元素。

 

指针访问数组的两种方式。

#include <stdio.h>

 

int main(void)

{

int array[10] = {1,2,3,4,5,6,7,8,9,10};

int i = 0;

int *p = array;

or(i = 0;i < 10;++i)

printf("%d\t",*(p+i));

printf("\n");

for(i = 0;i < 10;++i)

printf("%d\t",*p++);//把*p++ 看成*(p = p + 1);先使地址自增。

printf("\n");

return 0;

}

 

数组与指针的关系。

 

数组和指针在什么情况相等?在数组作为函数形参时.

数组作为形参的三种形式

 

int fun(int array[10]);

int fun_1(int array[]);

int fun_2(int *array);

 

举例证明

#include <stdio.h>

 

void fun(int array[100])

{

printf("fun:\n");

printf("sizeof(array) = %d\n",sizeof(array));

}

 

void fun_1(int array[])

{

printf("fun_1:\n");

printf("sizeof(array) = %d\n",sizeof(array));

}

void fun_2(int *array)

{

printf("fun_2:\n");

printf("sizeof(array) = %d\n",sizeof(array));

}

int main(void)

{

int array[10] = {0};

printf("array = %d\n",sizeof(array));

fun(array);

fun_1(array);

fun_2(array);

return 0;

}

输出结果

array = 40

fun:

sizeof(array) = 4

fun_1:

sizeof(array) = 4

fun_2:

sizeof(array) = 4

 

在main函数内array的字节长度为40

为什么在这几个函数内部是4呢?

这就证明了 在这种情况下  指针 等效于 数组参数

还有不管你给数组形参多少容量,到编译时都转换为指针

 

 

 

指向二维数组的指针变量

竟然指针可以指向一维数组,那么就一定可以指向二维数组了。

 

二维数组的地址和指针的关系

 

#define N 3

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

int (*p)[N] = array;

int (*p1)[N][N] = array;

 

array 和 &array 表示整个二维数组的地址

 

array[n] 和 &array[n] 表示第n个一维数组的首地址

 

array[n][n] 表示第n个一维数组的第n个元素

 

&array[n][n] 表示第n个一维数组的第n个元素的地址

 

p+n   等效于 array 和 &array

 

*(p+n)+n  等效于 &array[n][n]

 

*(*(p+n)+n)  等效于 array[n][n]

 

*(*p1+n)  等效于 array 和 &array

 

*(*p1+n)+n 等效于 &array[n][n]

 

*( *(*p+n)+n ) 等效于 array[n][n]

 

指针法访问数组

 

①使用指向数组元素的指针

#include <stdio.h>

#define N 3

int main(void)

{

int i = 0;

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

int *p = &array;//p指向array的首地址

for(i = 0;i < N*N;++i)

{

printf("%d\t",*(p+i));

if(i % N == 2)

printf("\n");

}

return 0;

}

 

②使用一维数组型指针

 

#include <stdio.h>

#define N 3

int main(void)

{

int i = 0;

int j = 0;

int array[N][N] = {{1,2,3},{3,4,5},{6,7,8}};

int (*p)[N] = array;//指向一维数组的指针,该一维数组的容量N

 

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("[%p] = %d\t",&array[i][j],array[i][j]);

printf("\n");

 

}

 

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("%d\t",*(*(p+i)+j));//输出array[i][j]的值

printf("\n");

}

printf("\n2:\n");

for(p = array;p < array+N;++p)

{

for(j = 0;j < N;++j)

printf("%d\t",(*p)[j]);//输出array[i][j]的值

printf("\n");

}

return 0;

}

 

③使用二维数组型指针

 

#include <stdio.h>

#define N 3

 

int main(void)

{

int i = 0;

int j = 0;

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

int (*p)[N][N] = array;

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("[%d] = %d\t",&array[i][j],*( *( *p+i )+j ));

printf("\n");

}

return 0;

}

 

二维数组作为形参

 

二维数组的地址也可以用做函数的参数。

使用数组作为形参的关键是能够成功的从实参得到有效数组地址。

 

在传递二维数组的地址时,可以使用三种不同的方式。

 

①使用指向数组元素的指针,声明方式如下:

int fun(int *p);

 

代码:

 

#include <stdio.h>

#define N 3

 

void print(int *p)

{

int i = 0;

int j = 0;

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("%d\t",*p++);

printf("\n");

}

}

 

void reverse(int *p)

{

int i = 0;

int j = 0;

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("%d\t",*(p+j*N+i));

printf("\n");

}

}

int main(void)

{

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

printf("输出数组:\n");

print(array);

printf("翻转数组:\n");

reverse(array);

return 0;

}

 

 

②使用指向一维数组的指针,声明方式如下:

int fun(int (*p)[]);

 

#include <stdio.h>

#define N 3

 

void print(int (*p)[N])

{

int i,j;

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("%d\t",*(*(p+i)+j));

printf("\n");

}

}

 

void print_1(int (*p)[N])

{

int i,j;

for(i = 0;i < N;++i,++p)

{

for(j = 0; j < N;++j)

printf("%d\t",*(*p+j));//p++等效于 p+i

printf("\n");

}

}

int main(void)

{

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

printf("print:\n");

print(array);

printf("print_1:\n");

print(array);

return 0;

}

 

③使用指向二维数组的指针,声明方式如下:

int fun(int (*p)[n][n]);

 

#include <stdio.h>

#define N 3

void print(int (*p)[N][N])

{

int i,j;

for(i = 0;i < N;++i)

{

for(j = 0;j < N;++j)

printf("%d\t",*( *(*p+i)+j ));

printf("\n");

}

}

 

void print_1(int (*p)[N][N])

{

int i,j;

for (i = 0; i < N; ++i)

{

for (j = 0; j < N; ++j)

printf("%d\t", *(**p + i * N + j));//不太理解

printf("\n");

}

}

 

int main(void)

{

int array[N][N] = {{1,2,3},{4,5,6},{7,8,9}};

printf("print:\n");

print(array);

printf("print_1:\n");

print_1(&array);

return 0;

}

 

指针与字符

 

字符指针:指向字符的指针,其跟指向int型变量的指针一样的声明。

char c = ‘n’;

int *p = &c;

 

字符指针访问

这些都跟int型指针一样。

int c = ‘c’;

int *p = &c;

printf(“%c\n”,*p);

 

指针与字符串

 

 

指向字符串的指针。声明方式如下:

 

char str[] = “Hello,world”;

char *p = str;

 

char *pstr = “Hello,world”;

 

指针访问字符串

 

例1

#include<stdio.h>

 

int main(void)

{

char str[] = “Hello,world”;

char *p = str;

 

printf(“%s\n”,p”);

return 0;

}

 

例2

#include <stdio.h>

 

int main(void)

{

char *p = "Hello,world";

printf("%s\n",p);

return 0;

}

 

访问字符串中的某的元素。

#include <stdio.h>

 

int main(void)

{

char *p = "Hello,world";

int i = 0;

for(i = 0; *p != '\0';++i)

printf("%c",*p++);

return 0;

}

#include <stdio.h>

 

int main(void)

{

char *p = "Hello,world";

int i = 0;

 

for(i = 0;p[i] != '\0';++i)

printf("%c",p[i]);

printf("\n");

return 0;

}

 

 

演示指针字符和字符串的一些特性

 

/* 字符指针和字符串 */

#include <stdio.h>

 

int main(void)

{

char * p1 = "Hello, world!";

char * p2 = "Hello, world!";

char * p3 = "Hello, world";

 

printf("%p\n", p1);//4个字节

printf("%d\n", sizeof(p1));//①

 

printf("%p\n", p2);//4个字节

printf("%d\n", sizeof(p2));//①

 

printf("%p\n", "Hello, world!");//①

printf("%d\n", sizeof("Hello, world!"));//这个是14字节

 

printf("%p\n", p3);//另开辟了一个地址

printf("%d\n", sizeof(p3));//4个字节

 

return 0;

}

//①处是同一个地址

 

 

指针数组:存储地址的数组

 

指针数组的声明:char *p_array[n];

 

指针数组的赋值:

char *p_array[3] = {NULL};//都指向空

 

char *p_array[3] = {“printf”,”scanf”,”hello”};

 

char *p_array[3] = {NULL};

p_array[0] = “printf”;

p_array[1] = “scanf”;

p_array[2] = “hello”;

 

试例:

#include <stdio.h>

 

int main(void)

{

char *p_array[3] = {NULL};

p_array[0] = "printf";

p_array[1] = "scanf";

p_array[2] = "hello";

int i = 0;

for(i = 0;i < 3;++i)

puts(p_array[i]);

return 0;

}

 

额额,指针难啊!!打算先放一边。


本文固定链接: http://www.devba.com/index.php/archives/2963.html | 开发吧

报歉!评论已关闭.