【C复习】

指针

指针变量类型的意义

取地址操作符(&)

c 复制代码
int a=0;
&a//可以得到存储a的地址

指针的解引⽤

为了存储a的地址可以存储起来方便下次使用,使用指针来存储

c 复制代码
int n=0;
int *a=&n;

那么如何修改存储的值呢

使用解引用操作符

c 复制代码
int n=0;
int *a=&n;
*a=10;

这样n的值就改为了10

指针变量的⼤⼩

c 复制代码
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *))

32位平台下地址是32个bit位,指针变量⼤⼩是4个字节

64位平台下地址是64个bit位,指针变量⼤⼩是8个字节

解引用的区别

但是char解引用可以修改1个字节
int
解引用可以修改4个字节

指针±整数

char*±一步跨1字节

int*±一步跨4字节

void* 指针

void*可以避免类型转换不兼容

指针运算

指针± 整数

*(arr+1);

指针往后移动一个单位(int为4字节)

指针 - 指针

指针相距多少个单位

指针的关系运算

<表示一个指针在另一个指针之前

const指针

const 修饰指针变量

c 复制代码
 int *const p;//const在*右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。
 int const *p;//const int*p
 //const在*左边,修饰的是指针指向的内容,
 //保证指针指向的内容不能通过指针来改变。
 //但是指针变量本⾝的内容可变。

野指针

成因

1.未初始化

2.越界访问

3.指向的空间释放了

如何避免

1.初始化置null

2.避免指针越界

3.不用时置null,用时检查有效性

4.避免返回局部变量的地址

数组名的理解

特殊情况:

1.sizeof(arr)表示整个数组

2.&arr表示整个数组

其他情况都表示首元素的地址

使⽤指针访问数组

*(p+1)等价于p[i]

字符指针变量

c 复制代码
char*
const char*//如果内容相同,地址就相同,因为存储在常量区

C/C++会把常量字符串存储到单独的⼀个内存区域,

当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始

化不同的数组的时候就会开辟出不同的内存块。

指针数组

是存放指针的数组。

数组里面全都是存放指针的

数组指针

是指向数组的指针

c 复制代码
int * p1[10];///[]优先级高于*,先与[]结合,是数组
int (*p2)[10];//先与*结合,是指针

函数指针

函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。

函数是有地址的,函数名就是函数的地址

函数指针变量

通过函数指针调⽤指针指向的函数。

c 复制代码
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
//回调函数
void calc(int(*pf)(int, int))
{
int ret = 0;
int x, y;
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
//在calc()中传入函数地址
}

sizeof和strlen的对⽐

sizeof (操作符)

只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。

strlen (库函数)

求字符串⻓度.str 中这个地址开始向后, \0 之前字符串中字符的个数。

库函数的模拟实现

memcpy

c 复制代码
void mymemcpy(void*des,cont void*src,size_t num)
{
  assert(des);
  assert(src);
  void*res=des;
  while(num--)
  {
     *(char*)des=*(char*)src;
     des=(char*)des+1;
     src=(char*)src+1;
  }
  return res;
}

strcpy与它的区别就是遇到\0时结束拷贝

memmove

c 复制代码
void mymemove(void*des,const void*src,size_t num)
{
  assert(des);
  assert(src);
  void*res=des;
  if(src<=des||(char*)src+num<=(char*)des)
  {
    des=(char*)des+num-1;
    src=(char*)src+num-1;
    while(num--)
  {
     *(char*)des=*(char*)src;
     des=(char*)des-1;
     src=(char*)src-1;
  }
  }
  else
  {
    while(num--)
  {
     *(char*)des=*(char*)src;
     des=(char*)des+1;
     src=(char*)src+1;
  }
  }

  return res;
}

strstr

c 复制代码
char* mystrstr(const void*str1,const void* str2)
{
  assert(str1);
  assert(str2);
  char*cp=str1;
  while(*cp)
  {
    char*s1=cp;
    char*s2=str2;
    while(*s1&&*s2&&*s1==*s2)
    {
    s1++;
    s2++;
    }
    if(!*s2)return cp;
    cp++;
  }
  return NULL;
}

自定义类型

内存对齐

原因:
  1. 平台原因 (移植原因)
  2. 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。不然可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
结构体内存对齐规则
  1. 对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  2. 默认值
    VS 中默认的值为 8
    Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  3. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
    体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

联合

联合的⼤⼩⾄少是最⼤成员的⼤⼩。

当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

数据存储

整形存储

原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。

大小端及如何判断

区分

⼤端(存储)模式:

是指数据的低位字节内容保存在内存的⾼地址处,⽽数据的⾼位字节内容,保存在内存的低地址处。

⼩端(存储)模式:

是指数据的低位字节内容保存在内存的低地址处,⽽数据的⾼位字节内容,保存在内存的⾼地址处。

代码判断

c 复制代码
//联合体判断
union myUnion
{
int a;
char c[4];
};
int main()
{
myUnion un;
un.a=0x12345678;
if(un.c[0]==0x12)
printf("大端存储");
else
printf("小端存储");
return 0;
   
}
c 复制代码
//截断判断
int main()
{
int a=0x12345678;
char*pa=(char*)&a;
if(*pa==0x12)
printf("大端存储");
else
printf("小端存储");
return 0;
}

浮点数存储

V=(-1)sM 2E

  1. (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
  2. M 表⽰有效数字,M是⼤于等于1,⼩于2的
  3. 2E 表⽰指数位
    1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表⽰⼩数部分。

类型提升和截断

运算时提升:小类型 → 大类型,顺序:

char/short → int → long → long long

float → double → long double

类型提升

当不同大小 / 类型的变量做运算时,编译器会自动把小范围的类型转换成大范围的类型,保证计算不丢失精度。也叫隐式类型转换、自动类型提升。

类型截断

把大范围类型强行塞进小范围类型里,装不下的高位数据直接丢掉。

编译链接

#define

原则:多加括号

编译链接的过程

  1. 预处理(进⾏宏替换/去注释/条件编译/头⽂件展开等)gcc --E hello.c --o hello.i
  2. 编译(⽣成汇编) gcc --S hello.i --o hello.s
  3. 汇编(⽣成机器可识别代码) gcc --c hello.s --o hello.o
  4. 链接(⽣成可执⾏⽂件或库⽂件) gcc hello.o --o hello
相关推荐
JavaWeb学起来2 小时前
Python学习教程(一)环境安装,基本数据类型,变量
开发语言·python·python基础
迷藏4942 小时前
# 发散创新:用 Rust实现高性能测试框架的底层逻辑与实战演练
java·开发语言·后端·python·rust
chushiyunen2 小时前
python单例模式、大模型一次加载多次复用
开发语言·python·单例模式
skywalk81632 小时前
训推一体化的AI飞桨套件:paddlex初探,还是不太顺利
开发语言·paddle
浮尘笔记2 小时前
PHP中常规通用接口验签加密规则设计
开发语言·后端·网络安全·php
lly2024062 小时前
《其他 W3C 活动》
开发语言
福楠2 小时前
现代C++ | 智能指针
c语言·开发语言·c++
ruxingli2 小时前
GoLang channel管道
开发语言·后端·golang
Risehuxyc2 小时前
PHP 的缓存机制
开发语言·缓存·php