指针
1.计算机内存的最小单位使BYTE(字节)
(1)每个BYTE的内存都有一个唯一的内存编号,这个编号就是内存地址
(2)编号在32位系统下是一个32位的整数,编号在64位系统下是一个64位的整数
(3)用指针型变量来表示这个内存地址号。
2.*的作用:
Typename * variableName:声明一个指针变量。变量名为variableName,该指针地址处的内容类型为Typename
3.&:得到变量对应的指针变量(内存地址)
void main{
int a=3;
int p; // p是int型变量,p是指针型变量
p = &a; // 指针型变量就是内存号,所以&符号取得a的内存号
int b = p;
printf("%d",b);
p = 100;
printf("%d",a) // 通过内存号修改变量值
char chs[10];
printf("%u,%u,%u,%u",chs,chs[0],chs[1],chs[2]) // %u:unsigned无符号整形,数组首地址,第一个元素的地址,第二个元素的地址
int ints[10];
printf("%u,%u,%u,%u",ints,ints[0],ints[1],ints[2]) // %u:unsigned无符号整形,数组首地址,第一个元素
}
4.无类型指针:
(1)void p:说明p只是一个指针变量,而不去指向任何一个变量
(2)空指针的写法: int *p = NULL
#include <stdio.h>
void main(){
int a = 100;
int p; // int p是声明写法:整体代表一个地址号,这个地址号上存着整数。单独一个p:定义写法,也是内存号
int q;
p = &a;
//int p = 1; // p指向的地址号是1(声明+定义写在了一起)
printf("%u",p); // 3852910012
printf("%u",p); // 100
printf("%d\n",sizeof(p)); //8:指针在64位系统下就是一个64位的整数,所以占8字节。无论指针指向什么类型的数据,64位下都占8字节
printf("%d\n",sizeof(a)); //4:int型占4字节
int *p; //声明指针,没有指向,就是野指针。野指针使导致程序崩溃的主要原因
*p = 1; // 为地址上赋值1,会出现错误 [1] 6285 segmentation fault (core dumped) a.out
int *p = NULL; // 空指针使合法的,野指针是非法的
}
/** int p 与p一致 /
#include <stdio.h>
void main(){
int a =100;
int p = &a;
int q;
q = &a;
printf("p:%u,q:%u\n",p,q); // p:2373644580,q:2373644580
printf("p:%d,q:%d",p,q);// p:100,q:100
}
5.指针的兼容性
#include <stdio.h>
void main(){
int a = 0X1013;
char b = 13;
int *p;
p=&a;
printf("%x",*p); // 1013
p=&b; // 10130d 用int指针指向char类型变量,因为int指针指向了连续4字节的地址,所以除了1013外后面还有3个字节的数据当做一体来指向
printf("%x",*p);
int *q;
char buf[] = {0x12,0x34,0x56,0x78,0x90,6,7,8,9,10};
q = buf;
printf("%x\n",*q);
//指针不兼容的情况
float i = 3.14;
int *p = &i;
printf("%d",*p); // 此时并不能打印出3,这就是指针类型不兼容
}
6.指向常量的指针和指针常量
void main(){
int a = 100;
const int p = &a; // 指向常量的指针只能读内存,但可以改变指向
printf("%d\n",p); // 100
int *const q = &a; // 常量指针可以读写指向的内存,但不能改变指向
*q = 200;
printf("%d\n",*q); //200
printf("%d\n",a); //200
}
7.数组与指针
(1)数组名也是一个指针变量,指向数组第一个元素。通过数组名+1,来指向数组中的第二个元素。
(2)指针运算不是简单地数值加减法,而是指针指向的数据类型在内存中所占字节数为倍数的运算。
void printarr(char s[]){
int i;
for(i=0;i<10;i++){
printf("s[%d]\n", %d);
}
}
void main(){
char buf[10] = {1,2,3,4,5,6,7,8,9,0};
char p = buf;
char p1 = &buf[0];
char *p2 = &buf[1];
printf("%d\n", *p); //1
printf("%d\n", *p1); //1
printf("%d\n", *p2); //2
p2 ++; // 指针是地址号,p+1
*p2 = 100; // 更改为100
printarr(buf);
}
(3)ip地址在网络中不是以字符串传输,字符串太长,而是以DWORD传输(4字节),int类型也占4字节。
#include<stdio.h>
void change2char(int i){
unsigned char p = &i;
printf("%d.%d.%d.%d",p,(p+1),(p+2),(p+3));
}
void main(){
int a = 0;
unsigned char p1 = &a; // 无符号使得最大值为255
p1 = 192;
// (p+1) = 168;
// (p+2) = 0;
// (p+3) = 1;
p1++;
p1 = 168;
p1++;
p1=0;
p1++;
p1=1;
printf("ip:%d",ip);
// a已经被改变为一个ip地址
change2char(a);
}
------------------
int s2ip(char s[]){
int a = 0;
int b = 0;
int c = 0;
int d = 0;
sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d);
printf("a=%d,b=%d,c=%d,d=%d\n", a,b,c,d);
int ip=0;
char p = &ip;
p++;
p = a;
p++;
p=b;
p++;
p=c;
p++;
p=d;
}
void main(){
char s[] = "192.168.0.1";
printf("%d\n", s);
}
eg:用指针合并数组
void main(){
char s1[] = "hello";
char s2[]="world";
char p1 = &s1;
char p2 = s2;
while(p1){ //p1指向的元素为0时跳出循环
p1 ++;
}
while(p2){
p1++ = *p2++; // 先指向,后指针移位
}
}
(3)二维数组的指针
void main(){
int buf[2][3] arr = {{1,2,3},{4,5,6}};
// int p[3] ; //定义一个指针数组
int (p)[3]; // 定义了一个指针,指向int[3]这种数据类型,也叫作指向二维数组的指针
printf("%d\n",sizeof(p)) ; //8 因为指针在64位系统都是占8位
printf("%d,%d\n",p,p+1); //相差12字节
p = buf; //p指向了二维数组的第一行
p++; //p指向了二维数组的第二行
// 指针遍历
int i,j;
for(i=0;i<2;i++){
for(j=0;j<3;j++){
//printf("%d\n", *(*(p+i))); //打印结果1,1,1,4,4,4
printf("%d\n", *(*(p+i)+j); // 遍历每个元素
printf("%d\n", p[i][j]); // 每个元素,与上面相等
}
}
}
指针作为函数参数
void test(int n){
n++;
}
void test2(int p){
(p)++;
}
void main(){
int i =100;
test(i); //c语言是值传递,所以普通函数参数的改变是不能传回给参数的,但如果参数是地址,改变地址上的值
printf("%d\n", i);
test2(&i);
printf("%d\n", i);
}
一维数组作为参数的指针
void swap(int a,int b){
int tmp = a;
a = b;
b = tmp;
}
// 当数组名作为参数传递时,数组名解析为数组首地址指针,所以多写为void setarray(int buf)
void setarray(int buf[]){
printf("占%d位\n", sizeof(buf)); //8:64位系统指针占8字节
buf[0] = 100;
buf[1]=200;
int i;
for(i=0;i<10;i++){
printf("%d\n", buf[i]);
}
}
void setarray(int buf,int n){ // n为数组的长度
buf[0] = 100;
buf[1]=200;
int i;
for(i=0;i<n;i++){
printf("%d\n", buf[i]);
}
}
void main(){
int a = 10;
int b = 20;
swap(&a,&b);
printf("%d,%d\n", a,b);
// 因为数组名是数组的首地址,所以数组名作为行参传递的时候,就相当于传递的指针
int buf[]= {1,2,3,4,5,6,7,8,9,0};
printf("%d\n", sizeof(buf)); //40
setarray(buf,sizeof(buf)/sizeof(int)); // 数组长度写成sizeof(buf)/sizeof(type)
}
二维数组的指针作为函数参数
void printarr2(const int (*p)[4],int m,int n){//mn分别是二维数组的第一个和第二个角标。通过const标识,保护函数不会改变数组中的值
int i,j;
for(i=0;i<m;i++){
for(j=0;j<n;j++){
printf("p[%d][%d] = %d\n", i,j,p[i][j]);
}
}
}
int main{
int buf[][4] = {{1,2,3,4},{5,6,7,8}};
printarr2(buf,sizeof(buf)/sizeof(buf[0]),sizeof(buf[0])/sizeof(int));
return 1;
} 33 min 18 s
计算二维数组每一列的平均值
#include<stdio.h>
void main(){
int buf[2][4] = {{1,2,3,4},{5,6,7,8}};
for(int i=0;i<4;i++){
int sum = 0;
for (int j = 0; j < 2;j++)
{
sum += buf[j][i];
}
printf("%d\n", sum);
}
}
8.函数指针
(1)函数也有地址,这个地址存放的是代码
(2)函数名同数组名一样,都是即表示本身,又表示指向自身开头的指针
(3)函数指针可以单独声明:ReturnType (func)(p1Type,p2Type)
(4)函数指针是指向函数的指针变量
void p(int,char) : 定义一个函数,函数名为p,参数为int和char类型,返回值为void类型
void (p)(int,char*):定义一个指针,该指针指向函数。返回值为void, 参数为int和char类型
c int getmax(int x, int y){ // 也可以写为int getmax(int x, int y) 意思一样 return x>y?x:y; } int getmin(int x, int y){ return x<y?x:y; } int main(){ int (p)(int,int) ; //定义一个指针p指向函数,该函数反获知是int,2个输入参数是(int,int) int fun = 0; scanf("%d",&fun); if(fun==1){ p = getmax; //函数名就是指针 }else{ p = getmin; //函数名就是指针 } int i = p(2,4); // 通过函数指针调用函数 printf("%d\n", i); }
(5)将一块内存初始化为0最常见的方法:
a. memset(buf,值,size(buf)):要设置的内存地址,设置的值,内存大小(单位字节)
b. memcpy(buf2,buf1,sizeof(buf1)):将buf1的内容全部拷贝到buf2
c. memmove(buf2,buf1,sizeof(buf1));
#include <string.h>
#include <stdio.h>
void main(){
int buf[10] = {1};
memset (buf,0,sizeof(buf));
int buf2[] = {1,2,3,4,5,6,7,8,9,0};
int buf3[];
memcpy(buf3,buf2,sizeof(buf2));
for (int i = 0; i < 10; i++)
{
printf("%d\n", buf2[i]);
}
}
9.指针数组与多级指针
(1)一个经典的错误模型:
c void getheap(int p){ p = malloc(sizeof(int)10); } void main(){ int *p = NULL; getheap(p); // 此处是错误的,因为函数的参数是值传递,虽然语义是getheap(p),但是mallo的内存地址只是付给了函数的形参,函数退出后,形参的值并不能传递给实参(指针的值也一样不能传递),导致p还是空指针,下面的p[0]=1发生空指针异常 p[0] = 1; printf("%d\n", p[0]); free(p); }
(2)经典的正确模型:二级指针作为形参,对上面错误模型的改写
c void getheap(int **p){ p = malloc(sizeof(int)10); // 所致地址号的内容,就是指以及指针的内容 } void main(){ int *p = NULL; getheap(&p); p[0] = 1; printf("%d\n", p[0]); free(p); }
10.main函数的一些解释:
(1)c语言main函数是有2个输入参数的,第一个表示输入参数的个数,第二个是一个指针数组,每个指针指向char类型
(2)输入参数个数永远大于1,因为程序名本身作为一个输入参数
(3)指针数组:这个指针数组的长度是第一个参数值,每个元素指向一个输入参数
int main(int argc,char *args[]){
if(argc!=4){
printf("请输入完整的数学表达式,以空格分开" );
return 0;
}
int a = atoi(args[1]);
int b = atoi(args[3]);
switch(args[2][0]){
case '+':
printf("%d\n", a+b);
break;
case '-':
printf("%d\n", a-b);
break;
case '*':
printf("%d\n", a*b);
break;
case '/':
if(b){
printf("%d\n", a/b);
}
break;
}
return 0;
}