C语言-指针

指针

在计算机中,内存空间的最小单位为字节,操作系统会为每一个字节内存空间编号,并且这个编号在当前程序中是唯一的。

读写内存的两种方式

  1. 通过变量名读写内存

变量本质上是一块有名字的内存空间,可以通过变量名读写对应的内存空间

  1. 通过内存地址读写内存

在计算机内存中,每一个字节内存空间都有一个编号,这个编号被称为内存地址。通过该地址可以读写对应的内存空间。而变量的本质是一块有名字(也有编号)的内存空间,在32位系统下,这个编号是一个四字节的整数。

&变量名。由于这种方式得到的内存地址就是变量所对应的内存空间地址,又是通过变量名得到的,因此可以称为“变量地址”,变量地址本质上就是内存地址。

#include<stdio.h>
int main(void)
{
	int i = 10;
	printf("%p",&i);//输出i的内存地址
	getchar();
	return 0;
}

用于保存内存地址的变量,称为指针变量。在 C 语言程序中不仅变量有类型,数据也是有类型的,例如: 1(整数)、 3.14(浮点数)、 ’c’(字符),需要使用与之匹配的类型变量进行保存。同理,内存地址也是一种数据,这种数据都是指针类型,因此需要指针类型变量来保存这种数据。

指针变量的定义与初始化

定义指针变量的一般形式为:类型名 *变量名; (也可以写做 类型名* 变量名,但是不推荐,*加在变量名前,更能看出其为指针),类型名表示该指针变量只能保存该类型变量的地址, *表示该变量是指针变量只能保存地址数据,变量名即该变量的名称。

指针变量和普通变量初始化方式相同,可以在变量定义时初始化,也可以先定义后初始化。

例如:
int a=10;
int *p_a=&a; //定义 int 指针变量 p_a,并将变量 a 地址赋值给 p_a
或者
int a=10;
int *p_a; //先定义 int 指针变量 p_a
p_a=&a; //然后将变量 a 地址赋值给变量 p_a

整数变量 a 的地址赋值给指针变量 p_a,就认为 p_a 指向了变量 a 。变量 a 中存储的是整数 10,而变量 p_a 中存储的是变量 a 的地址。想要访问数据 10,必须先找到指针变量 p_a,通过变量 p_a 中的数据&a,再找到变量 a,最后访问数据 10。

输出p,才是输出地址值

输出*p,是输出p对应的内存地址中的值

改变p对应的内存地址中的值,并不会改变p对应的内存地址

#include<stdio.h>
int main(void)
{
	int i = 999;
	
	int *p = &i;

	printf("%p\n", &i);//十六进制输出i的地址值,会补齐0
	printf("%d\n", &i);//十进制输出i的地址值,不会补齐0
	printf("%d\n", p);//十进制输出i的地址值,不会补齐0
	printf("%d\n", *p);//输出999,即*p指向的内存地址中的值
	printf("%d\n", i);//输出999,即i的数值

	*p = 888;
	
	printf("%d\n", *p);//输出888,即*p指向的内存地址中的值
	printf("%d\n", p);//十进制输出i的地址值,不会补齐0
	printf("%p\n", &i);//十六进制输出i的地址值,会补齐0
	printf("%d\n", i);//输出888,即i的数值

	getchar();
	return 0;
}

在 C 语言中,函数参数不仅可以是字符型、整型、浮点型……等,还可以是指针类型,作用是将变量地址传递给函数形参。

主要目的就是:函数内部修改外部变量的值。

#include<stdio.h>

int fun(int *i){
	*i = 6;
	printf("%d\n", *i);//输出6
	printf("%d\n", i);//输出变量i的内存地址,要注意这里只有指针类型的变量,这里的i不是main方法中的i,而是传入的形参
}


int main(void)
{
	int i = 10;
	printf("%d\n", i);//输出10
	fun(&i);

	printf("%d\n", i);//输出6

	getchar();
	return 0;
}

通过指针,来交换两个变量的值

#include<stdio.h>

int fun(int *i, int *j){
	int temp = *i;
	*i = *j;
	*j = temp;
}


int main(void)
{
	int i = 10;
	int j = 20;

	fun(&i, &j);

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

	getchar();
	return 0;
}

scanf函数

scanf 函数原型: int scanf(const char * _Format, …)

头文件:#include<stdio.h>

参数列表:

_Format :格式控制字符串,与 printf 函数中格式控制作用相同。

… :地址列表,由若干个地址组成。

功能: 获取用户按键输入的数据,并以指定格式写入到变量、或数组中。

#include<stdio.h>
int main(void)
{
    int a = 2;
	printf("请输入第一个数:\n");
	scanf("%d", &a);//从键盘输入一个整数,写入变量 a 中
	printf("变量a的值为%d",a); //输出变量a的值
	getchar();//接收使用 scanf 时按下的回车键

	getchar();//程序暂停,等待用户输入
	return 0;
}

从键盘获取多个数据时,相邻数据之间可以使用空格、回车、tab 键作为两个数据之间的分隔符。

如果在 scanf 函数中的格式控制字符串中除了占位符之外,还有其他字符,则在输入时也必须在对应的位置上输入相同的字符。

如下,scanf 函数中,格式控制字符串中为"%d+%d",则键盘上输入数字的时候,数字中间也要加上+

int main(void)
{
    int a;
	int b;
	printf("请输入两个数:\n");
	scanf("%d+%d", &a, &b);
	printf("变量a,b的值为%d,%d",a,b); //输出变量a,b的值
	getchar();
	getchar();
	return 0;
}

使用 scanf 获取字符串时,只需传入字符数组名即可,取地址符&可以省略不写。

数组与指针

数组本质上是一片连续的内存空间,每个数组元素都对应一块独立的内存空间,它们都有相应的地址。因此,指针变量既然可以指向变量,也就可以指向数组元素。

在 C 语言中数组可以看作是相同类型变量的集合。通俗点讲,数组中每个元素类型都是相同的。数组本质上是一片连续的内存空间,数组元素又可以看作是单独的内存空间,因此每个数组元素也都有自己的内存空间地址,简称数组元素地址。

输出p_a1和p_a2的值,可以发现,两个值相差总为4,就是一个int类型的数所占的4个字节数

#include<stdio.h>
int main(void)
{
    int a[5]={1,2,3,4,5}; 
	int *p_a1; //定义指向int变量的指针变量p_a1
	int *p_a2;
	p_a1=&a[0]; //把数组中第一个元素的地址赋值给指针变量
	p_a2=&a[1];
	printf("%d\n", p_a1);
	printf("%d", p_a2);
	getchar();
	return 0;
}

在 C 语言中,数组名与数组首元素地址等价。

指针加减法

指针本质上就是内存地址,在 32 位操作系统下,内存地址只是 4 字节的整数。既然是整数,就可以进行加、减、乘、除……等算术运算。不过需要注意,在 C 语言中一般只讨论指针加、减运算,乘、除等其他算术运算是没有意义。

在实际开发中,指针加、减多用于数组(或者连续内存空间)。当指针变量 p 指向数组元素时,p+1 表示指向下一个数组元素,p-1 表示指向上一个数组元素。注意加减运算不是 “移动一个字节”,而是移动一个“单元”,对于 int 来讲一个单元是 4 个字节。

#include<stdio.h>
int main(void)
{
    int a[5]={1,2,3,4,5}; 
	int *p_a1; //定义指向int变量的指针变量p_a1
	int *p_a2;
	p_a1=&a[0];
	p_a2= p_a1 + 2;
	printf("%d\n", p_a1);//输出数组第一个元素的内存地址
	printf("%d\n", p_a2);//输出数组第三个元素的内存地址,与第一个元素的内存地址相差8
	printf("%d\n", *p_a2);//输出数组第三个元素的值
	getchar();
	return 0;
}

总结:
1) 两个指针之间的减法表示:相差的单元的个数。
2) 两个指针之间的加法,没有意义;
3) 指针+普通整数表示指针向后挪 n 个单元;指针-普通整数表示指针向前挪 n 个单元;

数组也可以作为函数参数进行传递,但是数组做函数参数时,是没有副本机制的,只能传递地址。也可以认为,数组做函数参数时,会退化为指针。

#include<stdio.h>
//给函数传递的数组类型的形参,等同于传递此数组的指针
 void getSize(int p_nums[])
 {
 int size=sizeof(p_nums);
 printf("size=%d\n",size);//输出4,int类型的指针所占字节数

 printf("%d\n", &p_nums);//输出的是形参对应的指针的地址值(此地址值和数组的地址值是不相同的)
 printf("%d\n", &p_nums[0]);//输出的是实参对应的数组的地址值(数组第一个元素的地址值)

 }

 int main(void)
 {
 int nums[5]={1,2,3,4,5};
 int size=sizeof(nums); //计算数组 nums 总字节数
 printf("%d\n", &nums[0]);//输出的是实参对应的数组的第一个元素的地址值
 printf("%d\n", &nums);//输出的是实参对应的数组的地址值(数组第一个元素的地址值)
 printf("size=%d\n",size);//输出20
 getSize(nums);

 getchar();
 return 0;
 }

由于数组做函数参数时,会退化为指针,导致无法在被调函数中计算传入的数组大小以及长度。为了解决这种问题,规定数组做函数参数时,必须传入数组长度

除了在声明数组时候 sizeof(数组名)和 sizeof(指针变量名)的不同,其他时候“数组名” 和“指针变量名”用法都是一样的

#include<stdio.h>
 int main(void)
 {
 int nums[]={1,2,3,4,5};
 int *p_nums = nums;//直接将数组赋值给指针类型的变量p_nums,此时p_nums保存的是数组的地址值

 printf("%d\n", p_nums[4]);//输出数组的第五个元素
 printf("%d\n", nums[4]);//输出数组的第五个元素

 getchar();
 return 0;
 }

指针所占字节数和电脑的操作系统位数有关,32位的系统,指针是4个字节

#include <stdio.h>   
int main(void)  
{  
    int a=1;  
    char b='a';  
    float c=1.0;  
    void *p;  
    p=&a;  
    printf("a的地址为:0x%x,其字节数为:%d\n",p,sizeof(p));//输出4
    p=&b;  
    printf("b的地址为:0x%x,其字节数为:%d\n",p,sizeof(p));//输出4  
    p=&c;  
    printf("c的地址为:0x%x,其字节数为:%d\n",p,sizeof(p));//输出4  
	getchar();
    return 0;  
}

void* 类型的指针

void*类型的指针,能够接收任意类型的地址,但是不可以直接进行解引用的操作,需要强转为某个指针类型。void*类型的指针也不能直接进行加减的操作

字符串处理函数

strcpy

函数原型: char *strcpy(char *dest, char *src);
头文件: #include<string.h>
参数列表: dest:目标字符数组。 src:源字符串。
功能: 把 src 指向的字符串复制到 dest 指向的字符数组中。
返回值: 返回 dest 指向的内存地址。

src数组和dest数组是两个不同的数组,有不同的内存地址。假如src字符串中有\0,那么strcpy函数就只会拷贝\0之前的字符串。

#include <stdio.h>   
#include <string.h>

int main(void)  
{  
    char *src="test";
	char dest[10]={0};
	strcpy(dest, src);
	printf("%s\n",src);
	printf("%s\n",dest);
	getchar();
	return 0;
}

strcpy只能实现字符串的拷贝,函数参数和返回值都只为char类型

memcpy

函数原型: void *memcpy(void *dest, const void *src, int size);
头文件:#include<string.h>
参数列表: dest:目标地址空间 src: 源地址空间 size: 要复制的字节个数
功能: 从 src 指向的内存空间起始位置开始,拷贝 size 个字节到 dest 指向的内存空间中。
返回值: 返回 dest 指向的目标地址。

memcpy除了字符串可以拷贝,其他的数据类型也能拷贝,比如结构体类型等

#include <stdio.h>   
#include <string.h>

int main(void)  
{  
	int src[] = {1,2,3,4,5};
	int dest[10] = {0};
	int i;
	//8表示8个字节,即只拷贝src数组的前两位给dest数组
	memcpy(dest,src,8);
	
    //for循环中的i也要预先定义
	for(i = 0; i < 5; i++){
		printf("%d\n", dest[i]);
	}

	getchar();
	return 0;

}
strcmp

字符串比较函数

函数原型:int strcmp(char *str1,char *str2);
头文件:#include<string.h>
参数列表: str1:字符串 1, str2:字符串 2
功能: 从 str1 与 str2 的首字符开始逐个比较(比较字符的 ASCII 码),直到出现不同的字符或遇到’\0’为止。

返回值: (1) 字符串 str1 小于 str2,返回值为负数。 (2) 字符串 str1 等于 str2,返回值为 0。 (3) 字符串 str1 大于 str2,返回值为正数。所谓字符串的大小,就是当出现不同的字符时,比较此字符的ASCII码,此字符ASCII码大的,字符串大。

ANSI 标准规定,返回值为正数、负数、0 。而确切数值根据不同的C会有变化,不是所有的此函数的返回值都是0,1,-1。

#include <stdio.h>   
#include <string.h>

int main(void)  
{  
	char str[] = "abcd";
	char str1[] = "abxd";

	int i = strcmp(str,str1);
	printf("%d", i);//输出-1,表示str比str1要小

	getchar();
	return 0;

}
stricmp

忽略大小写的字符串比较函数

strcmp 与 stricmp 用法基本相同,stricmp 只是忽略了大小写进行比较而已。

#include <stdio.h>   
#include <string.h>

int main(void)  
{  
	char str[] = "abcd";
	char str1[] = "ABCD";

	int i = stricmp(str,str1);
	printf("%d", i);//输出0,两字符串相等

	getchar();
	return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值