C语言指针(一)
一、指针基本用法
1.1 指针用途
① 使程序简洁、紧凑、高效
② 有效地表达复杂的数据结构
③ 动态分配内存
④ 得到多余一个的函数返回值
1.2 地址和变量
在计算机内存中,每一个字节单元都有一个编号,称为地址。指针指向地址,所以指针可以理解为就是地址。(内存以字节为单位 int *p 假如p指向0x00000000 p+1指向0x00000004)
1.3 指针变量的定义
在c语言中,内存单元的地址称为指针。
<存储类型> <数据类型> *<指针变量名>
char *p;
指针说明指定的数据类型不是指针的数据类型,而是指针目标(*p)的数据类型,比如上面(*p)的类型为字符型char。
1.4 指针变量的赋值
指针变量在使用前不仅要定义说明,还要赋予具体的值(地址),不然会出现段错误等情况。未经赋值的指针变量不能随意使用。
如下演示未赋值的情况:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int *q;
scanf("%c",q); //未赋值 没有初始地址,这里执行会出现段错误
printf("%c",*q);
return 0;
}
如何规避这个问题?
1、对指针进行初始化 2、使用指针前进行非空判断 3、指针使用完成后将指针置空避免出现野指针
C语言提供&地址符来表示变量的地址,我们可以int a = 0; int *p = &a;
这样给p一个初始地址。当然也可以这样写,int a = 0; int *p; p = &a;
因为只有在定义时需要加*,后面*p全是代表取指针(地址)中存放的内容。
我们对上面的示例进行优化:
#include <stdio.h>
int main(int argc, char const *argv[])
{
char i = 0;
char *q = &i; /*规避野指针要点1、指针使用前初始化*/
if (NULL == q) /*规避野指针要点2、指针使用前非空判断*/
{
printf("q is NULL\n");
return -1;
}
scanf("%c",q);
printf("%c\n",*q);
q = NULL; /*规避野指针要点3、指针使用完成赋空*/
return 0;
}
1.5 指针变量的引用
指针指向的内存区域中的数据称为指针的目标。&是取地址运算符 *是指针运算符,两个运算符互为互逆操作,例如"&a"是取变量a的地址,*b就是取指针变量b所指向内存单元的内容。
如下程序演示:
#include <stdio.h>
int main(int argc, char** argv)
{
int m = 10;
int *p = &m;
printf("&m:%p,p:%p\n",&m,p); //p = &m
printf("m:%d,*p:%d,*(&m):%d\n",m,*p,*(&m)); // m = *p = *(&m)
return 0;
}
注意:指针本身也需要地址存放的,&p也会占用一个地址。
二、指针的运算
指针运算是以指针变量所存放的地址量作为运算量而进行的运算; 指针运算的实质就是地址的计算; 指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算;
2.1指针算术运算
px+n演示:
#include <stdio.h>
int main(int argc, char** argv)
{
int m = 10;
double n = 20.0;
int *p = &m;
double *q = &n;
printf("m = %d , &m= %p , p = %p , p+2= %p\n",m,&m,p,p+2);
printf("n = %f , &n= %p , q = %p , q+2= %p\n",n,&n,q,q+2);
return 0;
}
如上所示,p为int型指针,p+2就是p移动42字节 q为float型指针,q+2就是移动82字节。
p-q是两指针只想地址位置直接相隔数据的个数,而非字节个数,如下所示:
#include <stdio.h>
int main(int argc, char** argv)
{
int array[5] = {1 , 2, 3, 4, 5};
int *p = &array[0];
int *q = &array[4];
printf("q-p = %ld\n",q-p);
}
2.1指针关系运算
两指针直接的关系运算,表示它们指向地址之间的关系运算,见下表。
注意以下两点:
①具有不同数据类型的指针之间的关系运算没有意义,指向不同数据区域的数据的两指针之间,关系运算也没有意义。
②指针与一般整数变量之间的关系运算没有意义。但可以和零进行等于或不等于的关系运算,判断指针是否为空。
关系运算演示如下,实现字符串的逆序输出:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char s[] = "welcome";
char *p = NULL , *q = NULL , t = 0;
printf("%s\n", s);
p = s;
q = s + strlen(s) - 1;
while(p < q)
{
t = *p;
*p = *q;
*q = t;
p++;
q--;
}
printf("%s\n", s);
return 0;
}
三、指针与数组
3.1 指针与一维数组
1、指针与数组
① 在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
② 一维数组的数组名为一维数组的指针(起始地址),如a[8]的起始地址就是a
③ 设指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元数),则:x[i] 、*(px+i)、*(x+i) 和px[i]
具有完全相同的功能:访问数组第i+1个数组元素
指针与数组的异同:①指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量。②但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针是地址常量,也就是没有数组名+1这种写法,但是可以指针+1,如下演示情况。
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int array[10] = {0};
array = array + 1;
return 0;
}
指针与一维数组关系综合演示:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int a[] = {9 , 1 , 34 , 7 , 3 , 10} , i = 0 , n = 0;
int *p = a;
n = sizeof(a)/sizeof(int);
for ( i = 0; i < n; i++)
{
printf("%d %d %d %d\n" , *(a+i),a[i],*(p+i),p[i]);
}
return 0;
}
指针与数组常见等价操作:
3.2 指针与多维数组
3.2.1 相关概念
① 多维数组就是具有两个或两个以上下标的数组
② 在C语言中,二维数组的元素连续存储,按行优先存,基于这个特点,可以用一级指针访问二维数组
3.2.2 一级指针遍历二维数组
编程实现使用一级指针遍历二维数组:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
int *p = a[0]; //a[0]是第一行首地址 等价于&a[0][0]
int i=0,j = 0;
for(i = 0;i<2;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",*p++);
}
putchar('\n');
}
return 0;
}
3.2.3 行指针(数组指针)遍历二维数组
存储行地址的指针变量,叫做行指针变量。形式如下: <存储类型> <数据类型> (*<指针变量名>)[表达式]
例如,int a[2][3]; int (*p)[3]; 方括号中的常量表达式表示指针加1,移动几个数据。 当用行指针操作二维数组时,表达式一般写成1行的元素个数,即列数。二维数组名常被称为行地址。
指针和二维数组关系:
示例:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[2][4] = {{1,2,3,4},{5,6,7,8}};
int (*p)[4];
p = a;
printf("%p %p \n",p,p+1); //p+1 行指针移动一行 一行4*4=16bytes 地址间隔16bytes
printf("%p %p \n",*p,*p+1); //*p = p+0 *p+1 = *(p+0)+1 = &a[0][1] 所以间隔1个数据 地址间隔4字节
printf("%d\n",*(*(p+1)+1)); //p[1][1] = a[1][1] = 6
printf("%d\n",*(*(a+1)+1));
return 0;
}
四、综合练习
1、输入char型“123”,转为整数123输出
答:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char *s = "123";
int num = 0;
while ('\0' != *s)
{
num = num*10 + *s - '0';
s++;
}
printf("num = %d\n", num);
return 0;
}
2、编写一个函数,将整型数组中的n个数按反序存放
答:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[] = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10};
int n = sizeof(a)/sizeof(int);
int *p = a;
int *q = a + n - 1;
int temp = 0;
for (int i = 0; i < n/2; i++)
{
temp = *q;
*q = *p;
*p = temp;
p++;
q--;
}
for (int j = 0; j < n ; j++)
{
printf("a[%d] = %d ", j , a[j]);
}
printf("\n");
return 0;
}
3、编程实现,使用行指针遍历二维数组?
答:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[2][4] = {{1,2,3,4},{5,6,7,8}};
int (*p)[4];
p = a;
printf("%p %p \n",p,p+1);
printf("%p %p \n",*p,*p+1);
for(int i = 0;i<2;i++)
{
for(int j = 0; j<4;j++)
printf("%d ",*(*(p+i)+j));
putchar('\n');
}
return 0;
}
ps:欢迎大佬指正,共同学习进步。