C/C++指针深析

本文详细探讨了C++中的指针概念,包括const修饰指针、一维和二维数组指针的区别、作为函数参数和返回值的应用,以及结构体指针和内存对齐等内容,帮助初学者理解和掌握指针的复杂性。
摘要由CSDN通过智能技术生成

C/C++指针深析

指针对于初学者往往是很难以琢磨的东西,因为它并不如变量那么抽象,而是更贴近底层的真实结构。指针操作往往会出现各种各样的岔子,最常见的便是”segmentation fault”。所以这里辨析了各种指针类型,实践出真知,下面的例子如果自己敲下来就更好了😁😁😁

const修饰指针

#include <iostream>

using namespace std;

class Rectangle{
  public:
  Rectangle(int length, int width){chang=length; kuan=width;}
    void SetLength(int length){ chang=length;}
    int GetLength() const{ return chang;}
  private:
    int chang;
    int kuan;
};

int main(){
  // int a=123;
  // const int *p=&a;//const 修饰*,p可以改
  // cout<<*p<<endl;
  // int b=567;
  // p=&b;
  // cout<<*p<<endl;
  // int const *p1=&a;
  // int * const p2=&b;const 修饰p2,*p2可以改
  // *p2=789;
  // // p2=&a;
  // cout<<*p2<<endl;

  Rectangle *rec=new Rectangle(123, 456);
  Rectangle *rec1=new Rectangle(789, 456);
  cout<<rec->GetLength()<<endl;

  const Rectangle * pRec=rec;
  cout<<pRec->GetLength()<<endl;
  pRec=rec1;//同p
  cout<<pRec->GetLength()<<endl;//只能调用const方法
  Rectangle * const pRec1=rec;//同p2
  cout<<pRec1->GetLength()<<endl;
  Rectangle *pPrec0=rec;
  pPrec0->SetLength(789);//非const方法也可以调用
  cout<<pRec->GetLength()<<endl;

}

一维数组指针的区别

a代表首地址,在sizeof和&a时当成了数组整体

#include <iostream>
using namespace std;
#define sz(type) cout<<sizeof(type)<<endl;
int main(){
  int a[3]={12,13,14};
  sz(a);
  cout<<a<<endl;
  cout<<&a<<endl;
  cout<<&a[0]<<endl;
  /*
  12
  0x9ffdb4
  0x9ffdb4
  0x9ffdb4
  */

  cout<<endl;
  cout<<a<<" "<<a+1<<" "<<a+2<<endl;            //a = &a[0] != &a
  cout<<&a<<" "<<&a+1<<" "<<&a+2<<endl;
  cout<<&a[0]<<" "<<&a[0]+1<<" "<<&a[0]+2<<endl;
  /*
  0x9ffdb4 0x9ffdb8 0x9ffdbc  //步长是4,一个int
  0x9ffdb4 0x9ffdc0 0x9ffdcc  //步长是12,三个int,也就是数组长度
  0x9ffdb4 0x9ffdb8 0x9ffdbc  //步长是4,一个int
  */
  return 0;
}

二维数组指针的区别

注意与一维指针类比区别开来。

指针有减法和比较运算,没有加法运算。

#include <iostream>
using namespace std;
#define sz(type) cout<<sizeof(type)<<endl;
int main(){
  int a[2][3]={
    {12,13,14},
    {112,113,114}
  };
  sz(a);
  sz(a[0]);
  sz(a[0][0]);
  cout<<a<<endl;
  cout<<&a[0]<<endl;
  cout<<a[0]<<endl;
  cout<<&a[0][0]<<endl;
  
  cout<<endl;//步长是三个元素的数组的长度
  cout<<"a: "<<a<<" "<<a+1<<endl;           //a = &a[0]
  cout<<"&a[0]:"<<&a[0]<<" "<<&a[0]+1<<endl;

  cout<<endl;//步长是一个int
  cout<<"a[0]: "<<a[0]<<" "<<a[0]+1<<endl;  //a[0] = &a[0][0]
  cout<<"&a[0][0]: "<<&a[0][0]<<" "<<&a[0][0]+1<<endl;

  cout<<endl;
  cout<<"&a: "<<&a<<" "<<&a+1<<endl;//步长是二维数组的长度
  return 0;
}

指针作为函数的参数

使用swap(交换两个变量的值)作为演示,实现Swap的四种方法。当数组名作为函数的参数来传递的时候,他的高贵的数组结构特性已经失去了,成了一个地地道道的只拥有4个字节的平民。

#include <iostream>
using namespace std;
#define SWAP(a, b, temp) (temp=a, a=b, b=temp);
void func(int *x, int *y){
  int z=*x;
  *x=*y;
  *y=z;
}
void func1(int &x, int &y){
  cout<<x<<" "<<y<<endl; 
  int z=x;
  x=y;
  y=z;
}
template<class T>
void Swap(T &a, T&b){
  T temp=a;
  a=b;
  b=temp;
}
int main(){
  float a=12.1, b=13.2;
  cout<<a<<" "<<b<<endl;

  // int c=a;
  // a=b;
  // b=c;
  float c;
  Swap(a, b);
  cout<<a<<" "<<b<<endl;
  return 0;
}

指针作为返回值

要作为返回值,必须保证调用后返回的指针不会销毁,否则就是野指针。

#include <iostream>
using namespace std;
int a[3];//全局变量默认初始化为0
int * func(){
  return a;
}
int main(){
  int a=1;
  cout<<func()[0]<<" "<<func()[1]<<endl;
  return 0;
}

字符数组指针

1、str和p是不相同的!!

#include <iostream>
#include <cstring>

int main() {
  char str[3]="ab";
  char *p=str;//此处可以修改str,因为str只是字符数组(使用了字符串字面量来初始化str),但是并不是常量
  size_t length = std::strlen(str);//遇到'\0'才会停止计数
  // 例如 char a[3]={'a', 'b', 'c'}, 使用strlen后的长度不是3!!!
  printf("%c-%s-%ld-%ld\n", *str, str, sizeof(str), strlen(str));
  // sizeof(str) 返回的是数组占用的内存大小,而 strlen(str) 返回的是字符串的长度。
  printf("%c-%s-%ld-%ld\n", *p, p, sizeof(p), strlen(p));
  // sizeof(p) 返回的是int型指针占用的内存大小,而 strlen(p) 返回的是字符串的长度。
  *p='A';
  *(p+1)='B';
  std::cout<<*str<<*(str+1)<<std::endl;
  
  return 0;
}

2、此处会有一个warning,因为“ab”是存储在常量区域的,不能修改,在使用时需要使用const来申明

#include <iostream>
#include <cstring>

int main() {
  char *p="ab";// 先在常量区保存好"ab",然后在栈区建立一个char *
  //   const char *p="ab";
  printf("%c-%s-%ld-%ld\n", *p, p, sizeof(p), strlen(p));
  // sizeof(p) 返回的是int型指针占用的内存大小,而 strlen(p) 返回的是字符串的长度。
  *p='A';
  *(p+1)='B';
  std::cout<<*p<<*(p+1)<<std::endl;
  
  return 0;
}

3、此处会显示error: assignment of read-only location ‘* p’

#include <iostream>
#include <cstring>

int main() {
  const char *p="ab";
  printf("%c-%s-%ld-%ld\n", *p, p, sizeof(p), strlen(p));
  // sizeof(p) 返回的是int型指针占用的内存大小,而 strlen(p) 返回的是字符串的长度。
  *p='A';
  *(p+1)='B';
  std::cout<<*p<<*(p+1)<<std::endl;
  
  return 0;
}

4、字符数组指针的初始化

#include <iostream>
#include <cstring>
using namespace std;
int main() {
  char a='a';
  char b='b';
  char *p=&a;

  char c[]={a, b, 'c'};
  printf("%p--%p--%c--%c\n", &a, &b, a, b);
  printf("%p--%p--%c--%c\n", c, c+1, *c, *(c+1));
  char *p2[]={&a, p, c};
  for(int i=0; i<3; i++){
    printf("%p--%p--%c\n", &p2[i], p2[i], *p2[i]);
  }
  return 0;
}

函数指针

主要要把指针括起来

#include <iostream>
using namespace std;
void func(){
  cout<<"func"<<endl;
}

int add(int a, int b){
  return a+b;
}
int main(){
  int a=123;
  int *p=&a;
  printf("%p--%p\n", &a, p);

  //无参
  void (*pFunc)()=&func;//要不要&,都可
  printf("%p--%p\n", func, pFunc);

  //带参
  int (*pFunc1)(int, int)=&add;
  printf("%p--%p\n", add, pFunc1);
  return 0;
}

函数指针数组

#include <iostream>
using namespace std;
void func(){
  cout<<"func"<<endl;
}

int add(int a, int b){
  return a+b;
}
int sub(int a, int b){
  return a - b;
} 
int mul(int a, int b){
  return a*b;
}
int div11(int a, int b){
  return a/b;
}
int main(){
  int a=1;
  int b=2;
  int *p=&a;
  printf("%p--%p\n", &a, p);

  int (*pFunc[4])(int, int)={add, sub, mul, div11};
  for(int i=0; i<4; i++){
    cout<<(*pFunc[i])(a, b)<<endl;
  }
  return 0;
}

函数指针深度辨析

!!!!!!!!!!!!!

#include <iostream>
using namespace std;
void func(){
  cout<<"func"<<endl;
}

int add(int a, int b){
  return a+b;
}
int main(){
  void (*pFunc)()=&func;
  printf("%p\n", &pFunc);
  printf("%p\n", pFunc);
  printf("%p\n", *pFunc);
  printf("%p\n", **pFunc);
  printf("%p\n", **pFunc);

  printf("----------\n");
  printf("%p\n", &func);
  printf("%p\n", func);
  printf("%p\n", *func);
  printf("%p\n", **func);
  printf("%p\n", **func);


  return 0;
}

结构体内存对齐

#include <iostream>
using namespace std;

#define sz(type) cout<<sizeof(type)<<endl;
struct stu{
  char c;
  short a;
  int *b;
};//8
struct stu1{
  char c;
  int *b;
  short a;
};//12
struct stu2
{
  double a;
  char b;
  int *c;
};//16
struct stu3
{
  char b;
  double a;
  int *c;
};//24

int main(){
  sz(stu);
  sz(stu1);
  sz(stu2);
  sz(stu3);
  return 0;
}

结构体指针

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

struct Stu{
  int a;
  char c[13];
};
int main(){
  Stu s;
  s.a=12;
  strcpy(s.c, "121");
  cout<<sizeof(s)<<endl;
  cout<<s.a<<"--"<<s.c<<endl;

  Stu *pStu=(Stu *)malloc(sizeof(Stu));
  pStu->a=11;
  strcpy(pStu->c,"qwe");
  cout<<pStu->a<<"---"<<pStu->c<<endl;
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值