C语言——指针

指针

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;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容