C/C++-指针
参考
1. 指针
适用于C/C++
指针与内存/地址
数据以二进制的形式存在内存中,每个数据占n个字节
每个字节都有一个内存地址
指针:就把他理解为一个数据,只要是数据就以二进制的形式存储在内存中
指针(在内存中存的值是 被指向的 那个数据的 内存地址值) – 见下图
且每个指针在内存中也有一个地址
总结:指针的值就是被指向数据的内存地址
CPU访问内存时需要的是地址,而不是变量名和函数名,变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,变量名和函数名都被替换成地址
编译和连接过程的一项重要的任务就是找到这些名称所对应的地址
通常:变量名表示的是数据本身;
函数名、字符串名、数组名是表示的是代码块和数据块的首地址
地址本质上是一个整数
4位或8位
指针使用
一个变量A 存放的是 另一个变量B的指针,那么将A变量 称为 指针变量
指针必须进行初始化!
指针创建
dataType* name=value
,dataType
表示该指针所指向的数据的类型
注意指针的类型是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++ – 面向对象