当前位置: 首页 > news >正文

2025/2/17--2/23学习笔记(week1)_C语言

1 整数的存储
  • 只有整数才有原码,反码,补码,原码取反加一(除了符号位)得到补码。补码的补码会变成原码。 在任何位运算里,都是操作的补码,因为整数在内存里都是以补码存储的
2 移位运算符
  • 移位运算符只能用与整数,且移动二进制位的补码,不改变这个数本身的值
  • 左移,左边丢弃,右边补零
  • 右移,分为逻辑和算数(一般)右移,与编辑器有关。1. 逻辑右移:左边⽤0填充,右边丢弃 2. 算术右移:左边⽤原该值的符号位填充,右边丢弃
  • 在进行完位运算后,得到的仍然是补码。
3 位运算符
  • 位操作符 &| ^ ~

  • & 有0则为0,同时为1则为1可以用真假来记

  • | 有1则为1,同时为0才是0

  • ^ 相同为0,相异为1

  • ~ 按位取反,包括符号位

  • 异或的一些技巧

    • 异或支持交换率,即与运算顺序无关 (3^3^5=3^5^3)

    • a^a=0 0^a=a

    • 基于以上特点,可以用于交换两个变量但不使用第三个变量

    • int a = 5;
      int b = 10;
      a = a ^ b;
      b = a ^ b;
      a = a ^ b;
      
4 求一个正数在内存里存储的二进制中的1的个数
  • 方法一:只能用于正数	
    	int count = 0;
    	int n = 15;
    	while (n>0) {
    		if (n % 2 == 1) {
    			count++;
    		}
    		n /= 2;
    	}
    	printf("%d", count);
    
    方法二:一个数在&1之后,如果==1,则说明该位是1,否则则为0
        int count = 0;
        int n = -1;
        for (int i = 0; i < 32;i++) {
            if ((n >> i) & 1 == 1) {
                count++;
            }
        }
        printf("%d", count);
    
    方法三:n = n & (n - 1)这样写执行一次该代码,会去掉二进制里最右边的一个1(相当于将最右边的一个1拆成0……1int count = 0;
        int n = -1;
        while (n != 0) {
            n = n & (n - 1);
            count++;
        }
        printf("%d", count);
    
    	
    
5 逗号表达式
  • (……,……,……)从左向右依次进行计算,整个表达式的结果是最后一个表达式的结果,常和while函数一起使用
6 结构体
  • 创建结构体
struct stu {
	int age;
	char name[10];
}s2,s3;
  • 初始化,使用{}struct stu s1 = {10,"zhangsan"};。也可以不按顺序初始化struct stu s4 = { .name = "lisi",.age = 20 };
  • 可以用 . 访问成员printf("%d", s1.age);
7 整型提升
  • C语言中整型算术运算总是至少以(int)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。(在 C 语言中使用char类型变量存储数字时,确实只能存储该数字二进制表示的前 8 位。)
  • 有符号整数提升是按照变量的数据类型的符号位来提升的
  • 无符号整数提升,高位补0
  • 在计算时提升,计算完在截断(char:只能在[-128,127])
  • 在以%d 打印一个char类型的变量的时候,会先对该变量进行整型提升得到补码,在转化为原码进行打印(一般默认是有符号char)
8 算数转换
  • 如果某个操作符的各个操作数属于不同的类型,那么除非其中⼀个操作数的转换为另⼀个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
  • 在比较有符号整型和无符号整型时,有符号整数会被隐式转换为无符号整数。转换的方式是保留二进制位不变,但是将其解释为无符号数。(例如,在 32 位系统中,有符号整数 -1 的二进制表示是 0xFFFFFFFF(补码形式)。当把它转换为无符号整数时,同样的二进制位 0xFFFFFFFF 会被解释为无符号数 4294967295。)
9 指针
  • 如果对一个(int)类型取地址,取到的是最小那个地址(一个int4个字节,每个字节都有自己对应的地址,&a则代表最小那一个地址)
  • 任何数字存入指针变量里,都会被当作指针处理
  • 一个指针变量在32位机器上是32个0/1组成的序列,需要4个字节来存储,在64位机器上是64个0/1组成的序列,需要8个字节来存储
  • 不同的指针类型,在解引用时访问的字节数与指针类型有关
  • 不同的指针类型,会影响+1的步长
10 void*指针
  • 泛型指针可以理解为⽆具体类型的指针(或者叫泛型指针),这种类型的指针可以⽤来接受任意类型地址。但不能对该指针进行解引用和±操作。经常用于接受不同类型的函数参数,在使用时可以先强制类型转化后在使用。
11 指针运算
  • 指针±整数: *(p+i)或者 scanf(“%d”, p + i),这里的p+i本身就表示一个地址

    • 不只可以加上一个整数,还可以减去一个整数,在这个程序里,int* p = &arr[9];此时p里面存放的是最后一个元素的地址,也可以从这里向前访问。

    • int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
      int* p = &arr[9];
      for (i = 0; i < sz; i++)
      {
          printf("%d ", *(p - i));
      }
      

  • 指针-指针:

    • 只有指向同一块区域的指针才可以相减(比如指向同一个数组的内存)

    • 相减的结果的绝对值是两个指针之间的元素个数

    • int arr[10] = { 0 };
      int n = &arr[0] - &arr[9];
      printf("%d ", n);
      
      
  • 指针的关系运算:(可以用这种关系运算来判断一个数组是否已经结束)

  • int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int* p = arr;
    while (p < &arr[sz]) {
    	printf("%d ", *p);
    	p++;
    }
    
12 strlen函数的实现
方案一:(计数器)
size_t my_strlen(char* str){
	size_t num = 0;
	while (*str != '\0') {
		num++;
		str++;
	}
	return num;
}

int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen(arr);
	printf("%zd", len);
	return 0;
}
方案二:(运用指针相减)
size_t my_strlen2(char* str) {
	char* start = str;
	while (*str != '\0')
		str++;
	size_t num = (size_t)(str - start);
	return num;
}
13 const修饰指针
  • 对于const int n = 10;会让n变成常变量,无法被修改,但仍然是一个变量。

    const修饰的变量不能直接被修改,但可以通过指针侧面进行修改
    const int n = 10;
    int* p=&n ;
    *p = 1;
    printf("%d ", n);
    
    如果不想让这个值被指针修改,只需要在int*前也加上const
    const int n = 10;
    const int* p=&n ;
    *p = 1;
    printf("%d ", n);
    
  • const int * p ,const放在左边,修饰的是整个(*p),也就是可以不能修改p指向的内容,但指针变量本身可以修改,即可以从指向a变为指向b

  • int * const p,const放在*的右边,修饰的是p本身,也就是不可以修改p本身,即p的指向,但可以通过p修改p指向的内容即 *p

14 野指针
  • 这里面就造成p变成了一个野指针,因为在test结束的时候,n空间就释放了
    int* test() {
    	int n = 100;
    	return &n;
    }
    
    int main() {
    
    	int* p = test();
    	printf("%d ", *p);
    
    	return 0;
    }
    
15 assert断言
  • assert包含在头文件<assert.h>里,如果在开头加上#define NDEBUG

会直接让程序里所有的assert断言都失效

  • int* p = &a; assert(p != NULL)如果p为NULL,会直接终止程序
  • 该断言只有在debug模式里才有用,在release模式则会忽略该语句。
16 指针与数组
  • 数组名是数组首元素的地址,但存在两个特殊情况
    • sizeof(数组名) 在这种情况下,数组名表示整个数组,计算出的结果是整个数组的大小,单位是字节
    • &数组名 这里的数组名也是表示整个数组,取出的是整个数组的地址(类型不是int*)
17 一维数组传参的本质
  • 在实际以数组传参的时候,传递的就是一个地址,也就是如果数组经过传参,就彻底变成首元素的地址,没有特殊情况了,不可以用sizeof(arr) / sizeof(arr[0])来计算数组长度了。所以如果想知道数组的长度,必须在传参的时候传过去
18 冒泡排序
  • 两两相邻的元素进行比较

  • 一趟解决一个数字,所以需要(n-1)次

  • void bubble_sort(int* pa,int len) {
    	for (int i = 0; i < len-1; i++) {//控制一共运行几次,如果有n个元素,只需要运行n-1次即可,因为最后一个元素会自行有序
             int flag = 0;
    		for (int j = 0; j < len-1-i; j++) {//控制在一次循环里比较几对数据
    			if (pa[j] > pa[j + 1]) {
    				int tem = pa[j];
    				pa[j] = pa[j + 1];
    				pa[j + 1] = tem;
                     flag = 1;
    			}
    		}
            if (flag == 0) {
    			break;
    	    }
    	}
    }//flag是为了优化代码,如果有一次外层循环一次都没有交换(flag==0),则说明已经有序,可以直接结束(break)
    
19 二级指针
  • int** p就是二级指针,int *说明p指向一个int *类型的数据,而第二颗 *则说明p是一个指针变量

  • int a = 10;
    int* pa = &a;
    int** ppa = &pa;
    
20 指针数组
  • 存放指针的数组

  • 模拟二维数组(与二维数组有不同,因为二维数组每一行之间是连续空间)

  • int main() {
    	int a[3] = { 1,2,3 };
    	int b[3] = { 4,5,6 };
    	int c[3] = { 7,8,9 };
    	int d[3] = { 10,11,12 };
    	int* arr[4] = { a,b,c,d };//数组名是首元素地址
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 3; j++) {
    			printf("%d ", arr[i][j]);
    		}
    		printf("\n");
    	}
    	return 0;
    }
    
21 字符指针
  • 在打印字符串的时候,只需要传入该字符数组首元素的地址const char* p = "hdjslds";printf("%s",p);

  • 字符指针可以被赋为字符数组,也可以直接用常量字符串赋值,两者的区别是常量字符串不可以被修改

  • char* p = "hdjslds";
    
    char arr[10] = "hdjslds";
    char* p = arr;
    
    
  • 对于同一个常量字符串,即使使用多个指针变量指向它,都指向的是同一块内存空间(即同一个常量字符串,因为它无法被修改,所以没必要创建多个)

  • const char* str1 = "hello";
    const char* str2 = "hello";
    if (str1 == str2) {
    	printf("same");
    }
    
22 数组指针变量
  • 指向整个数组的指针
  • int(*p)[5] 其中(*p)说明p是一个指针变量,[5]说明p指向的是一整个数组,5代表元素个数,int 代表指向这个数组里边存放的元素的数据类型
http://www.dtcms.com/a/35526.html

相关文章:

  • C语言进阶习题【3】(5 枚举)——找单身狗2进阶版本
  • 【前沿探索篇七】【DeepSeek自动驾驶:端到端决策网络】
  • AWS Bedrock平台引入DeepSeek-R1 模型,推动深度学习
  • 网站搭建wp
  • unity学习51:所有UI的父物体:canvas画布
  • AI大模型趣味实战专栏 预告篇
  • 文件上传-黑名单关键字绕过
  • 用C/C++绘制跳动的爱心:从数学方程到动画实现
  • 数据安全_笔记系列02:国密算法(商用密码算法)详解
  • JavaScript querySelector()、querySelectorAll() CSS选择器解析(DOM元素选择)
  • MySQL数据库——常见慢查询优化方式
  • 基于STM32单片机设计的宠物喂食监控系统
  • Zap:Go 的高性能日志库
  • Linux故障排查和性能优化面试题及参考答案
  • 计算机毕业设计SpringBoot+Vue.js古典舞在线交流平台(源码+文档+PPT+讲解)
  • DeepSeek-R1-Zero:基于基础模型的强化学习
  • 【备赛】点亮LED
  • P8615 [蓝桥杯 2014 国 C] 拼接平方数
  • 从零到一学习c++(基础篇--筑基期十一-类)
  • 电脑经常绿屏(蓝屏)怎么办(解决方法)?
  • Golang概述
  • 【Docker】如何在Linux、Windows、MacOS中安装Docker
  • HTTP代理与HTTPS代理的区别及HTTPS的工作原理
  • 基于SpringBoot的“流浪动物救助系统”的设计与实现(源码+数据库+文档+PPT)
  • 嗯,用户想开发一个竹类知识宝库小程序?
  • Docker 搭建 Redis 数据库
  • PCL 点云添加高斯噪声
  • 【Python量化金融实战】-第2章:金融市场数据获取与处理:2.1 数据源概览:Tushare、AkShare、Baostock、通联数据(DataAPI)
  • Unity实用技能-UI与粒子效果总结
  • JavaScript系列(85)--包管理工具详解