C/C++-指针

参考

https://www.bilibili.com/video/BV1s5411J78m?p=3&spm_id_from=pageDriver&vd_source=7155082256127a432d5ed516a6423e20 – 入门 讲的不错

1. 指针

适用于C/C++

指针与内存/地址

数据以二进制的形式存在内存中,每个数据占n个字节
每个字节都有一个内存地址

指针:就把他理解为一个数据,只要是数据就以二进制的形式存储在内存中
指针(在内存中存的值是 被指向的 那个数据的 内存地址值) – 见下图
且每个指针在内存中也有一个地址
总结:指针的值就是被指向数据的内存地址

CPU访问内存时需要的是地址,而不是变量名和函数名,变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,变量名和函数名都被替换成地址
编译和连接过程的一项重要的任务就是找到这些名称所对应的地址

通常:变量名表示的是数据本身;
函数名、字符串名、数组名是表示的是代码块和数据块的首地址

地址本质上是一个整数
4位或8位

指针使用

一个变量A 存放的是 另一个变量B的指针,那么将A变量 称为 指针变量
指针必须进行初始化!

指针创建

dataType* name=valuedataType表示该指针所指向的数据的类型
注意指针的类型是 dataType*而不是dataType

int *p; // 表示指针p指向的内容是整型
int* p; //表示指针p是一个int*类型
int a;
int *p; // *表示指针变量
p=&a; //p的值 时a的地址值

int *p=&a; // 等价
int* p=&a; // 等价

printf("%d",*p); // 取值

指针修改

int a;
int *p;
p=&a; //p的值 时a的地址值
// 等价于 int *p=&a;
// 等价于 int* p=&a;

a=1;
&p // p的地址值
*p // *表示取消引用,*p 取了p的值(地址)所指向的数据
*p=10; // a此时变成了5

char c;
char *pc;

double d;
double *pd;

// *p++ 等价于*(p++)
int a=10;
int* p=&a;
printf("%p\n",p);
printf("%d\n",*p++);
printf("%d\n",a);
printf("%p\n",p);
#include <stdio.h>

int main(){
    int a;
    a=10;
    int *p;
    p=&a;
    printf("%d\n",p); // 6422036 'python':hex(6422036) =='0x61fe14'
    printf("%p\n",p); //000000000061FE14 
    printf("%p\n",&a); //000000000061FE14

    int b=20;
    *p=b; // 只是值的修改
    printf("%d\n",a); // 20
    printf("%p\n",p); // 000000000061FE14
    printf("%p\n",&a); // 000000000061FE14 不会改变a的地址
    printf("%p\n",&b); // 000000000061FE10

    p=&b;
    printf("%p\n",p); // 000000000061FE10,指向了b
    printf("%p\n",&a); // 000000000061FE14
    printf("%p\n",&b); // 000000000061FE10

    return 0;
}

在这里插入图片描述

指针算数

指针的值+1,指针的值+dataType个字节
尽量不要对变量的指针进行运算,这样没有意义

int a;
a=10;
int *p;
p=&a;
printf("%p\n",&a); // 000000000061FE14
printf("%p\n",p+1); // 000000000061FE18
printf("%p\n",&a); // 000000000061FE14

*运算符优先级高于双目运算符(单目运算符优先级>双目运算符)

int a=1;
int* p=&a;
printf("%d\n",*p+1); // 2

指针的比较运算
对指针变量进行比较运算时,比较的是指针变量本身的值,也就是数据的地址,如果地址相等那么两个指针指向用一个数据,否则指向不同的数据

指针赋值

不同类型的指针之间,不能进行赋值int * 不能赋值给 double * 的指针

2.数组指针

数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关

数组/指针/sizeof

数组在内存中只是数组元素的简单排列,没有开始和结束标志
不能使用int *p=arr; sizeof(p)/sizeof(int),因为p只是一个指向int类型的指针,编译器并不知道它指向的到底是一个整数还是一些列的整数(数组),所以sizeof(p)求得的是p这个指针变量本身所占用的字节数,而不是整个数组占用的字节数。
指针指向的数据类型可以不同
指针变量占用的内存空间相同

一维数组与指针

数组指针:指向数组的指针
不同于指针数组!!!

数组名可以认为是做一个指针,指向数组的第0个元素
数组本身是指针这个说法不准确!!!
第0个元素的地址称为数组的首地址
数组指针 指向的是数组中的一个具体元素,而不是整个数组,所以指针数组的类型和数组元素的类型有关

数组指针与数组名不同,数组名不可以改变,而数组指针可以改变
(下面的具有 右结合性)
*p++ 等价于*(P++)(注意,运算时先*p后++),不能使用*arr++,因为数组名不能改变
*++p 等价于*(++P) 等价于*(p+1)
(*p)++只对数值进行改变

type *p=arr p+1相当于移动了一个type类型的字节

int a[3]={1,2,4};
int* p=a;
// 下面三个是等价的
printf("%p\n",p);
printf("%p\n",a);
printf("%p\n",&a[0]);

// printf("",a++); // 这个是错误的,数组名是不能改变的
printf("%d\n",*p++); // 1
printf("%d\n",*(p++)); // 1,*p赋值之后,再++
printf("%d\n",*p+1); // 2
printf("%d\n",*(p+1)); // 2 
printf("%d\n",*(++p)); //2 
printf("%d\n",*++p); //2 
int main()
{
    int a[3]={1,4,5};

    int *p=a;
 
    // printf("%d\n",*p++);
    // printf("%d\n",*p);
    printf("%d\n",(*p)++);
    printf("%d\n",*p);
    // printf("%d\n",*(p++));
    // printf("%d\n",*p);

    for (int i=0;i<3;i++)
    {
        printf("%d\n",a[i]);
    }
    return 0;
}

重要

[]符号具有取内容的作用
a[i],*(a+i),p[i], *(p+i) 这四个是等价的
&a[i],(a+i),&p[i], (p+i) 这四个是等价的

int a[3]={1,2,3};
for (int i=0;i<3;i++)
{
    printf("%d,%d,%d,%d\n",a[i],*(a+i),p[i],*(p+i));
    printf("%p,%p,%p,%p\n",&a[i],(a+i),&p[i],(p+i));
}

数组名/数组名地址

int main()
{
    int a[3]={1,2,3};
    printf("%p\n",a); // 0x61fe14 数组首元素地址 a的类型为 int
    printf("%p\n",&a); // 0x61fe14 整个数组的起始地址, 类型为int (*)[3]
    printf("%p\n",&a[0]); // 0x61fe14
    printf("%p\n",a+1); // 0x61fe18 = 0x61fe14 + 0x04 首元素下一个元素的地址
    printf("%p\n",&a+1); // 0x61fe20 = 0x61fe14 + 0x0C(12) 跳过了整个数组

	// int *p=&a; // 这种是错误的 类型不匹配
	
    return 0;
}
char str[10]="hello"
func(str) 等价于 func(&str)

多维数组指针

https://blog.csdn.net/angiusc/article/details/106599792

二维数组与一维数组的关系

a[2][3] 可以看成是2个一维数组,每个一维数组的数组名是a[0],a[1]
嵌套理解 a[3][4] 中 a[0]是一个数组(也是一个一维数组名)

// 可以定义1xX的数组
int a[1][3]={1,2,3};
for (int i=0;i<1;i++){
    // for (int j=0;j<3;j++){
    //     printf("%d\n",a[i][j]);
    // }
    printf("%p\n",a[i]); // &a[i][0] 与a[i] 等价
    printf("%p\n",&a[i][0]); // &a[i][0] 与a[i] 等价
    printf("%d\n",a[i][0]);
}

a[2][3] 可以看成是2个一维数组,每个一维数组的数组名是a[0],a[1]

对于一维数组及其指针 type *p=arr p+1相当于移动了一个type类型的字节
对于二维数组及其指针,p+1应该移动整个一维数组对应的字节,type (*p)[n],(*p) 表示指针,int [n] 表示指针指向的数据类型

int a[1][3]={1,2,3};
for (int i=0;i<1;i++){
    // for (int j=0;j<3;j++){
    //     printf("%d\n",a[i][j]);
    // }
    printf("%p\n",a[i]);
    printf("%p\n",&a[i][0]); // &a[i][0] 与a[i] 等价
    printf("%d\n",a[i][0]); // 这是数值
}

int a[2][3]={{1,2,3},{4,5,6}};
// 下面三个是等价的 
// *a[2][3] 可以看成是2个一维数组,每个一维数组的数组名是a[0],a[1]*
printf("%p\n",*a);
printf("%p\n",a[0]);
printf("%p\n",&a[0][0]);

// 下面是等价的
int (*p)[3]=a;
printf("%p\n",*(p+1));
printf("%p\n",*(a+1));
printf("%p\n",a[1]);
printf("%p\n",&a[1][0]);

重要

a=&a[0]
a+i=&a[i]
a[i]=&a[i][0]

*a=a[0]
*(a+i)=a[i]
*a[i]==a[i][0]

重要

a[i]==*(a+i)==p[i]==*(p+i)
a[i][j]==*(a[i]+j)==*(*(a+i)+j)==p[i][j]==*(p[i]+j)==*(*(p+i)+j)

int (*p)[3]=a;
for (int i=0;i<2;i++)
{
    for (int j=0;j<3;j++)
    {
        printf("%d,%d,%d,%d,%d,%d\n",a[i][j],*(a[i]+j),*(*(a+i)+j),p[i][j],*(p[i]+j),*(*(p+i)+j));
    } 
}

/*  /// 补充 ///
    // 理解
    对于1维数组
    int a={1,2,3};
    int* p=a;
    a[1]==*(P+1)==*(a+1)==p[1]
    &a[1]==p+1==a+1

    // 二维数组
    // 看成嵌套的一维数组
    int a[][2]={1,2,3,4};
    int (*p)[2]=a; 
    *(*(p+1)+1) ==a[1][1]
    p指向了一个都是指针的数组(首地址),而数组元素的每个指针,指向了一个一维数组(首地址)
    p+1==a[1]==&a[1][0]
    *(p+1)==a[1]==&a[1][0] // 这个记忆方法放到下面可以理解

    // 下面三个方法是等价的
    printf("%d\n",*(a[1]+1)); //a[1]也是一个指针,表示一维数组
    printf("%d\n",*(*(p+1)+1));  // *(p+1) p+1表示一个一维数组,
    printf("%d\n",a[1][1]);
*/ 

重要 - 第二种表达方法

int a[2][3]={{1,2,3},{4,5,6}};

int *p=&a[0][0]; // p=a p=&a[0] *p=a[0] a=&a[0] a[0]=&a[0][0]
for (int i=0;i<2;i++)
{
    for (int j=0;j<3;j++)
    {
        printf("%d,%d\n",a[i][j],*((p+i*3)+j));
    }
}

重要 – 补充

int arr[3]=[1,2,3];
int *p=arr; // 合法
// int (*p)[3]=arr; // 错误
int (*p)[3]=&arr; // 合法, 因为 int (*p)[3] 表示一个指向长度为3的整型数组的指针变量,&a表示的是一整个数组的地址

int arr[][3]=[1,2,3];
int (*p)[3]=&arr; // 合法

3.字符/字符串指针

指向字符串的指针
通常利用字符数组表示字符串

字符数组归根结底还是一个数组,所以数组大部分的功能都适用

char s[]="hello";
char* p=s;
for (int i=0;i<sizeof(s)-1;i++){
    printf("%c,%c,%c\n",s[i],p[i],*(p+i));
}

for (int i=0;i<strlen(s);i++){
    printf("%c,%c,%c\n",s[i],p[i],*(p+i));
}

另一种字符串表示方法

直接使用一个指针指向字符串
被称为:字符常量
能够修改指针指向的地址,但是不能修改内容

char *s="hello";

char *s;
s="hello";

s="world"; // 正确
// s[2]='1';// 错误

两种创建方式的不同

char s[]="xxx"以字符数组存储在全局数据区或栈区,具有读写权限
char* s="xxx"存储在常量区,只有读取权限,一旦定义不能修改
对于 字符串修改

https://blog.csdn.net/qq_31347869/article/details/105877116!!!
也就是数组形式的字符串可以进行字符串修改(具有读写权限)
指针形式的字符串不可以进行修改(只有读权限)

字符串数组形式与指针形式的区别(补充)

数组初始化是从静态存储区把一个字符串复制给数组
指针初始化只是复制字符串的地址
《c语言-函数 返回值与局部变量一节的例子》

char arr1[]="hello";
char *arr2="world";

两种字符串的补充

// 初始化注意事项
// char s1[10];
// s1="hello"; // 这种不行
char *s2;
s2="world"; // 这种可以

// 访问
char arr1[]="hello world";
char *arr2="hello cpp";

for (int i=0;i<11;i++)
{
    printf("%c,%c",arr1[i],*(arr1+i));
}
printf("\n");
for (int i=0;i<9;i++)
{
    printf("%c,%c\n",arr2[i],*(arr2+i));
}

/* 重要 */
printf("%s\n",arr1); // hello world
printf("%s\n",arr2); // hello cpp
printf("%s\n",arr1+1); // ello world
printf("%s\n",arr2+2); // llo cpp

*运算符优先级高于双目运算符

char a='c';
char* p=&a;
printf("%d\n",a); //99
printf("%c\n",*p+1); //d
printf("%d\n",*p+1); //100

对于字符串,整个引号中的内容作为指向该字符串存储位置的指针 !!!

// are字符串的第一个字符的地址
// 字符串可以当做指针*"family"相当于取指针指向的元素,取地址的元素,也就是该字符串的首元素
printf("%s,%p,%c\n","we","are",*"family"); // we,0000000000409001,f

遍历的一种写法

void func(char *str)
{
    while(*str) // '\0' 的ASCII 是0
    {
        *str=toupper(*str); // 自己赋值给自己
        str++;
    }
}

*char 字符串及其地址

char *ptr=new char[10];
printf("%#p\n",&ptr[0]); // 0x7d14d0
printf("%#p\n",ptr); // 0x7d14d0
printf("%#p\n",&ptr); // 0x61fe18
printf("%#p\n",(char *)ptr); // 0x7d14d0
printf("%#p\n",(void *)ptr); // 0x7d14d0

4. 其他指针

二级指针

https://blog.csdn.net/weixin_43408582/article/details/115254539

指向指针的指针
指针存放的是一个(普通)变量的地址,而该指针(变量)的地址也可以被另一个指针存放
指针变量也是变量,因此也需要存储空间

int a=100;
int* p1=&a;
int** p2=&p1; // 可以理解为 (int*)* p2=&p1 因为指向的是指针,而指针的类型是int*,而创建的又是一个指针,所以是(int*)* 

// 例子2
int a = 8;
int *p1 = &a;
int **p2 = &p1;

printf("a = %d\n",a);//打印a的值
printf("*p1 = %d\n",*p1);//*p等价于a,打印a的值

printf("&a = %p\n",&a);//打印a的地址
printf("p1 = %p\n",p1);//打印p1的值,即a的地址

printf("&p1 = %p\n",&p1);//打印p1的地址
printf("p2 = %p\n",p2);//打印p2的值,即p1的地址

printf("*p2 = %p\n",*p2);//p2一次解引用,*p2等价于打印p1的值,即a的地址
printf("**p2 = %d\n",**p2);//p2二次解引用,**p2等价于打印a的值

空指针

对未初始化的指针赋值为NULL
空指针是不指向任何数据的指针,是无效指针

// NULL 其实是一个宏定义,指向了内存的0地址,
#define NULL ((Void*)0)
char* s=NULL;

if (p==NULL){
	// ...
}

nullptr / NULL

https://blog.csdn.net/qq_53055705/article/details/115557721– 还没看

NULL

NULL是C/C++的预处理常量,通常被定义为0或(void*)0,它可以被隐式的转换为整型,但是这种转换可能会引起一些意想不到的问题,特别是在构造函数和模板函数等中
当使用NULL时,当出现一个需要指针类型的上下文时,编译器无法确定NULL应该转换成哪种指针类型,从而产生一些歧义,同时在某些平台上,将整数0赋给指针类型可能会导致不可预测的行为

nullptr

是C++11新引入的关键字,它是一个真正的空指针常量,而不是整型,nullptr可以与任何指针类型隐式的转换,但不会与整数类型隐式的转换,这样就避免了很多由于NULL造成的类型转换问题
nullptr表示一个类型安全的空指针,也可以与其他指针类型混用

void指针

void* 表示一个有效指针,指向实实在在的数据,只是数据的类型尚未确定,在后序使用过程中需要进行强制类型转换

char* s=(char*)malloc(sizeof(char)*30);

野指针

如果一个指针指向的内存没有访问权限,或者指向一块已经释放掉的内存,那么就无法对该指针进行操作,这样的指针就是野指针

free

free§并不能改变指针p的值,p依然指向以前的内存,为了防止再次使用该内存,建议将p的值手动置为NULL
free§ 只是释放掉动态分配的内存,p的值并不是NULL,仍然指向之前这个被释放掉的内存,所以if§仍然会执行,但是输出p指向的内存会报错

避免野指针

初始化为NULL
free之后,赋值为NULL

5. 指针与函数

https://blog.csdn.net/L_fengzifei/article/details/126291514

5.1 函数指针

使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数
returnType (*p)(param list)returnType为函数返回值类型

typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;

// 例子
typedef int (*PTR_TO_FUNC)(int, int);
int max(int a, int b){
    return a>b ? a : b;
}
PTR_TO_FUNC pfunc = max;
printf("max: %d\n", (*pfunc)(10, 20));
int max(int a,int b){
    return a>b?a:b;
}

int main(){
    int x=1;
    int y=2;
    int maxval;
    int (*pmax)(int,int)=max;

    maxval=(*pmax)(x,y);    
    printf("%d\n",maxval);
    
    return 0;
}

5.1.2 进阶

https://stackoverflow.com/questions/9552663/function-pointers-and-address-of-a-function!!!
https://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as!!!
https://stackoverflow.com/questions/37501982/address-operator-with-pointer-to-member-function!!!
https://blog.csdn.net/L_fengzifei/article/details/128118034

在编译时,每个函数都有一个入口地址,该入口地址就是函数指针所指向的地址,可以利用该指针变量调用函数
函数在内存中具有物理地址,该地址能够赋给指针变量

函数名:可以理解为地址,但是实际上不是地址。函数的首地址,是void()类型
&函数名:表示以一个指向该函数这个对象的地址 相当于一个指针(指向一个整体)void(*)()类型
这两个地址值是相等的
函数名在 非&函数名 的 表达式中被隐式转换成函数指针void(*)()类型

实际上 下面两种C标准都是可以的:

// 标准1
void func1(char *);
void (*ptr1)(char*);
ptr1=func1;
(*ptr1)("li")

// 标准2
void func2(char *);
void (*ptr2)(char*);
ptr2=func2;
ptr2("li")

例子重要!!!

#include <iostream>
using namespace std;

int func(int x)
{
    return x*x;
}

int main()
{
    // cout<<"hello world"<<endl;

    int (*pfunc1)(int)=func;
    int (*pfunc2)(int)=&func;

    printf("0x func %#p\n",func); // 0x func 0x4015a4
    printf("0x &func %#p\n",&func); // 0x &func 0x4015a4
    printf("0x pfunc1 %#p\n",pfunc1); // 0x pfunc1 0x4015a4
    printf("0x *pfunc1 %#p\n",*pfunc1); // 0x *pfunc1 0x4015a4
    printf("0x func2 %#p\n",pfunc2); // 0x func2 0x4015a4
    printf("0x *func2 %#p\n",*pfunc2); // 0x *func2 0x4015a4

    cout<<"func:"<<func(10)<<endl; // func:100
    cout<<"*func:"<<(*func)(10)<<endl; // *func:100
    cout<<"*pfunc1:"<<(*pfunc1)(10)<<endl; // *pfunc1:100
    cout<<"pfunc1:"<<pfunc1(10)<<endl; // pfunc2:100
    cout<<"*pfunc2:"<<(*pfunc2)(10)<<endl; // *pfunc2:100
    cout<<"pfunc2:"<<pfunc2(10)<<endl; // pfunc2:100

    return 0;
}

函数指针数组

char (*ptr())[5]; // ptr是一个函数指针,指向的函数 返回值 是一个由五个char元素构成的数组

// 区别
char (*ptr)[5]; // ptr是一个普通的指针,指向一个由五个char元素构成的数组

指针函数

下面是指针函数返回值是指针,指针指向的数据类型

char *strlong(char* s1,char* s2){
    if (strlen(s1)>=strlen(s2)){
        return s1;
    }
    else{
        return s2;    
    }
}

注意局部变量作为指针函数的返回值情况 https://www.cnblogs.com/xuhj001/p/3436175.html

函数运行结束后会销毁在他内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能引发运行时错误

// 下面的例子,有的编译器直接报错,提示 返回局部变量的指针
int *func()
{
    int n=100;
    return &n;
}

int main()
{
    int *p=func(),n;
    n=*p;
    printf("%d\n",n);

    return 0;
}

这个例子很重要https://blog.csdn.net/szm1234/article/details/120864801

6.结构体指针

一个指针变量指向结构体,叫做结构体指针
struct struct_name* var

struct str{
	char* name;
	int num;
} stu1={"tom",1};

struct stu* pstu=&stu1;

// 直接创建指针
struct str{
	char* name;
	int num;
} stu1={"tom",1},*pstu=&stu1;

// 使用指针获取结构体成员
(*pstu).name; // 必须加括号
pstu->name; // 直接通过结构体指针获取成员,这种方法更有效

结构体指针作为函数参数传递

‘C-语言函数中的例子’

6.1 结构体数组指针

因为是数组,所以数组名可以作为元素的首地址

struct stu class[]={
    {"li",2},
    {"wang",3}
};

// 下面两个是等价的,都表示地址
printf("%p\n",class);
printf("%p\n",&class[0]);

// 所以可以利用指针指向地址
struct stu* pstu=class; 

// 下面这两种访问方法都是等价的
struct stu* pstu=class;
for (int i=0;i<2;i++){
    printf("%s,%d\n",(pstu+i)->num,(pstu+i)->age);
}

for (int i=0;i<2;i++,pstu++){
    printf("%s,%d\n",pstu->num,pstu->age);
}

注意

结构体内的数组与指针 存在一定的危险

struct Name
{
    char name[10];
};

int main()
{
    struct Name person={"li"};
    cout<<person.name<<endl;

    // 下面这种是不行的 相当于已经分配了字符数组的空间,而用一个字符常量的地址进行赋值
    // 是因为有可能指针在分配空间的时候指向了一个不可修改的内存空间
    // struct Name person2;
    // person2.name="wang";
    // cout<<person.name<<endl;

    return 0;
}

改进

#include <iostream>
#include <cstring>
using namespace std;

void getinfo(struct names *pst);
void makeinfo(struct names *pst);
void showinfo(const struct names *pst);
void cleanup(const struct names *pst); // 注意 虽然是释放,但是可以是const

struct names
{
    char *fname;
    char *lname;
    int letters;
};

int main()
{
    struct names person; 
    getinfo(&person);
    makeinfo(&person);
    showinfo(&person);
    
    // 释放内存
    cleanup(&person);

    return 0;
}

void getinfo(struct names *pst)
{
    cout<<"input fname lname"<<endl;
    
    char temp[10];
    gets(temp);
    pst->fname= (char *)malloc(strlen(temp)+1); // strlen实际的字符个数,不加\0
    strcpy(pst->fname,temp); 

    gets(temp);
    pst->lname= (char *)malloc(strlen(temp)+1); // strlen实际的字符个数,不加\0
    strcpy(pst->lname,temp); 
}

void makeinfo(struct names *pst)
{
    pst->letters=strlen(pst->fname)+strlen(pst->lname);
}

void showinfo(const struct names *pst)
{
    printf("%s,%s,%d\n",pst->fname,pst->lname,pst->letters);
}

void cleanup(const struct names *pst)
{
    free(pst->fname);
    free(pst->lname);
}

6.2 结构体数组/指针 进阶

结构体名 不是该结构体的起始地址,更不是该结构体首元素的地址
结构体的起始地址,在值上 等于 该结构体首元素的地址

#include <iostream>

#define LEN 10

using namespace std;

struct names
{
    char first[LEN];
    char last[LEN];
};

struct people
{
    struct names handle;
    char favfool[LEN];
    char job[LEN];
    float income;
};


int main()
{
    // 结构体名 不是该结构体的起始地址
    // 结构体的起始地址,在值上 等于 该结构体首元素的地址
    struct names name1={"li","wang"};
    // std::cout<<name1<<endl;
    printf("%p\n",name1); // 0x61fd40 不等于首元素的地址
    printf("%p\n",&name1); // 0x61fdf0 相当于整个结构体的起始地址 而结构体的起始地址 的值 等于第一个成员的地址
    printf("%p\n",name1.first); // 0x61fdf0 
    printf("%p\n",&name1.first); // 0x61fdf0 该成员(整个成员)的起始地址
    printf("%p\n",&name1.first[0]); // 0x61fdf0 等价于 name1.first

    struct people people1={
        {"li","wang"},
        "apple",
        "ex",
        20.1
    };

    printf("%s\n",people1.handle.first); 
    printf("%s\n",people1.handle.last); 
    printf("%p\n",people1); // 0x61fd10 !!! 说明结构体名不等于首元素的地址也不等于结构体的起始地址
    printf("%p\n",&people1); // 0x61fdc0 整个外层结构体的起始地址
    printf("%p\n",people1.handle); // 0x61fd10 !!! 说明结构体名不等于首元素的地址也不等于结构体的起始地址
    printf("%p\n",&people1.handle); // 0x61fdc0 整个内存结构体的起始地址 等于 整个外层结构体的起始地址
    printf("%p\n",people1.handle.first); // 0x61fdc0 下面三个是数组的地址
    printf("%p\n",&people1.handle.first); // 0x61fdc0 
    printf("%p\n",&people1.handle.first[0]); // 0x61fdc0 
  
    struct people peoples[2]={
        {
            {"li","wang"},
            "apple",
            "ex",
            20.1
        },
        {
            {"zhang","san"},
            "pea",
            "ex2",
            22.1
        },
    };

    // 结构体指针
    struct people *ptr1;
    ptr1=&people1; // 结构体的名字并不等于结构体元素的首地址!!! 指向起始地址是对的,因为类型是匹配的
    cout<<ptr1->favfool<<","<<ptr1->job<<","<<ptr1->income<<endl;

    // 结构体数组指针 把他想象成普通数组的指针就行了
    struct people * ptr;
    // ac - 84 = int 44 一共44个字节
    cout<<&peoples[0]<<" "<<&peoples[1]<<endl; // 0x61fd80 0x61fdac 这个相等于起始地址
    cout<<&peoples<<endl; // 0x61fd80 整个数组的地址
    cout<<peoples<<endl; // 数组首元素的地址,也就是第一个结构体的地址
    // 结构体的名字并不等于结构体元素的首地址!!! 
    
    // version1
    ptr=peoples;
    cout<<ptr<<" "<<ptr+1<<endl; // 0x61fd80 0x61fdac
    cout<<ptr->job<<endl;
    
    // version2
    ptr=&peoples[0]; // struct people 所以+1 跨过了整个数组
    cout<<ptr<<" "<<ptr+1<<endl; // 0x61fd80 0x61fdac ptr+1跨过了整个结构体数组中的一个元素
    cout<<ptr->income<<endl;


    return 0;
}

???

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

#define N 2

struct stu{
    char name[10];
    int num;
}boya[N],boyb[N],*pa,*pb;


int main(){
    FILE* fp;
    int i;
    if ((fp=fopen("ex4.txt","wt+"))==NULL){
        puts("fail to open file");
        exit(0);
    }

    printf("input data\n");

    pa=boya;
    for (i=0;i<N;i++,pa++){
        scanf("%s %d",pa->name,&pa->num);
    }
    pa=boya; // 为什么要写两遍??
    for (i=0;i<N;i++,pa++){
        fprintf(fp,"%s %d\n",pa->name,pa->num);
    }
    return 0;
}

c++ 对象指针

见 c++ – 面向对象

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值