C语言指针笔记

一、取地址运算

1、地址意义

变量的储存位置

2、获取地址

运算符:&
用法:& + 变量名(变量类型)
作用:取得相应变量(变量类型)的地址

3、输出地址

利用%p
如下代码,可获取并输出变量a的地址

#include <stido.h>
int main(void){
	int a;
	printf("%p", &a);
	return 0;
}	

为编写方便,文章后续仅保留主函数内内容。

4、一些特性

a.编译器架构不同,变量地址的大小(所占字节数)不同。所以不能用int等一般变量类型的变量,储存地址

b.相邻变量的地址相差:该编译器下的变量地址大小(所占占字节数) 如:32位架构下,变量地址的大小为四个字节。则32位架构下相邻变量的地址大小相差四个字节

c.数组变量的地址与该数组第一个元素相同,且相邻元素的地址相差:该编译器下的变量地址大小(所占占字节数)

二、指针

1、前言

scanf()作为内置函数,可以实现输入变量的值的功能。

int a;
scanf("%d", &a);

注意到输入scanf()函数的是变量a的地址 &a,说明scanf()有两个特点:
第一,scanf()中存在能稳定接收变量a地址的某个变量
第二,scanf()有通过变量的地址访问改变函数外变量的能力。

所以C语言中存在“某个能保存变量的地址的变量”,并能通过这“某个变量”访问改变变量的值。这就是指针。

2、指针变量(专门储存变量地址的变量)

①定义指针变量

/*1*/int i1;
/*2*/int* i2;
/*3*/int *i3;
/*4*/int *i41, i42;
/*5*/int *i51, *i52;

如上列举一些可能的定义方式:
1号为普通int类型变量i1;
2、3号是等效的,表示指针i2、i3;
4号表示定义了一个指针i41一个普通int类型变量i42
5号表示定义了两个指针i51、i52
重点区分4、5号的不同,避免错误。

②函数中调用指针变量

void f(int *p){
	printf("%p", p);//此时p是变量i的地址
	printf("%d", *p);
	/*此时*p是变量i的值,*可以调用指针变量p储存地址的变量
	因而可以通过改变*p的值改变函数外变量i的值*/
	
}
int i=0;
void f(&i);

如上,在函数f中定义指针变量,输入变量i的地址,即可在函数f中访问变量i。
注意,只有指针变量p被输入变量的地址后,*p才有意义,可以赋值。
通过指针修改变量k的值的代码如下:

//定义指针p和变量k
int *p;
int k=0;
//将k的地址赋予p,此时*p指向变量k
p = &k;
//通过访问*p改变变量k的值
*P = 1;

不能直接:

int *p = 0/*此时p不表示任何变量的坐标,是随机值。*p不指向任何变量,无意义

③常量指针

①int* const q;表示指向某一变量后,不能再被修改。
②const int* q;表示不能通过*q去修改所指变量的值.
③非const的指针可以加上const,当作const指针传入函数,传入后函数内为const指针,函数外无影响。(避免函数对外面变量的修改)

int i;
//表示q只指向i,不能再指向其他变量,地址本身不能改变
int*const q = &i;
q++//无效
//表示q指向i,不能对*q赋值改变i的值,但是可以对i直接赋值
const int *q = &i;//const只要在*的前面即可。如:int const*q;
*q = 10;//无效
i = 10;//有效

3、指针的部分使用情景

①交换两个变量的值

void change(int *pa, int *pb){
	int t = *pa;
	*pa = *pb;
	*pb = t;
}

如上:change()函数可交换两变量的值。

②函数返回多个值

a.指针变量返回多个结果
void maxmin(int a[], int len, int *min, int *max){
	*min = *max = a[0]
	for(int i=0; i<len; i++){
		if(a[i] < *min){
			*min = a[i];
		}
		if(a[i] > *max){
			*max = a[i];
		}
	}

如上:maxmin()函数可以输出:输入数组中的最大值和最小值。

b.函数返回运算状态,结果通过指针返回
int divide(int a, int b, int *result){
	int ret = 1;
	if(b == 0){
		ret = 0;
	}
	else{
		*result = a/b;
	}
	return ret;
}

如上:divide()函数通过返回值ret判断除法是否成功,通过*result返回除法结果。

4、指针与数组

①数组变量本身表达地址,所以如下:

int a[10];
int *p=a;//无需用&取地址
p = &a[1]//但是数组的单元是变量,需要用&取地址

②[]运算符可以对数组做,也可以对指针做:

int a[10];
int *p=a;
//此时,p[0] == a[0]

③*运算符可以对指针做,也可以对数组做:

int a[10];
int *p=a;
//此时,*p == a[0]; *a == a[0]

④数组变量是const的指针(常量指针),所以不能被赋值。(不能等于其他数组变量)

5、指针运算

①指针与常数:

指针p加上n,实际上是对p所指的地址加上指针类型的字节数乘以n。计算后得到的p是原p后的第n个“变量”(可能存在,可能不存在)的地址。

int* p;
int a[10];
p = &a[0];
p+=5;//此时*p --> a[5]
p+=4;//此时*p --> a[9]

应用:利用*p++对数组进行遍历:

int* p;
int a[] = {1,2,3,4,5,6,7,8,9,-1};
p=a;
/* *运算优先级小于++,所以利用数组a最后一项,以下
循环可以提供一种新的遍历方式*/
for(*p != -1){
	printf("%d\n", *p++);
}

②指针与指针(仅同类型指针,抵制使用不同类型的指针进行运算):

指针p、q。(p - q)会得到p和q之间有几个“变量”(可能存在,可能不存在)的地址。

int* p, q;
int a[10];
p = &a[2];
q = &a[6];
int sum = p-q//此时sum = (6-2) = 4

③0地址(用NULL表示)

0地址的指针是无效指针,函数中可用0地址的指针表示返回的指针无效(?理论上,尚未经实践。。。)

int *p = NULL;//仅作演示,实际编程不应让指针为0值

6、malloc函数

需函数头:

#include <stdio.h>
#include <stdlib.h>

一般用于定义动态内存数组:
(用法及注意事项记录于注释)

int number;
int *a;
printf("输入所需变量数量:")scanf("%d", &number);
a = (int*)malloc(number*sizeof(int));
/*首先,malloc(n)表示向系统请求一块大小为n个字节
的内存,所以n = 变量数乘以变量类型的字节数。然后,
malloc的返回值的类型是void*,所以需要(变量类型*)
转化为所需的变量类型。*/
//上述操作完成后,可当成数组a[number]使用
free(a);
/*程序结束时需将请求的内存返回,注意不要更改指针a的
值,返回时a必须为请求的内存的第一个变量地址(别改a或者记得改回去就行)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值