C语言杂谈

news2024/11/25 2:59:47

在这里插入图片描述
努力扩大自己,以靠近,以触及自身以外的世界

文章目录

  • 什么是定义?什么是声明?什么是赋值?什么是初始化?
  • 什么是生命周期?什么是作用域?全局变量?局部变量?
  • sizeof是函数吗?关键字!!!
  • signed、unsigned 关键字
  • static关键字
  • abs函数和fabs函数
  • 空结构体占多大空间?
  • 柔性数组
  • union
  • enum
  • typedef
  • 取整和取模
  • 内存对齐——为什么需要内存对齐?
  • 宏定义#define
  • 指针和数组的关系?
  • 向特定地址中写入数据?
  • #和##
  • assert是宏而不是函数
  • malloc申请0字节空间
  • 函数参数的传递发生在函数调用之前
  • 可变参数列表

什么是定义?什么是声明?什么是赋值?什么是初始化?

定义:定义就是创建一个对象,为这个对象分配一块内存并给它取上一个名字,这个名字就是我们经常所说的变量名或对象名

声明:告知编译器这个变量名已经被占用,所有的变量声明时不能设置初始值,因为声明时并没有给出存储空间

赋值:给开辟好的空间赋上数据

初始化:一种特殊的赋值,在变量创建的阶段给上数据,初始化只能有一次

// 定义并初始化全局变量
int global = 10;

// 函数声明
void myFunction();

int main() 
{
    // 声明并初始化局部变量
    int local;
    local = 20;

    // 调用函数
    myFunction();

    // 打印全局变量和局部变量的值
    printf("Global: %d\n", global);
    printf("Local: %d\n", local);
    return 0;
}

// 函数定义
void myFunction() 
{
    // 赋值操作
    global = 30;
}

什么是生命周期?什么是作用域?全局变量?局部变量?

生命周期:从开辟到释放所经历的这一时间段

作用域:变量的有效作用范围

全局变量:在整个程序的任何地方都是可用和可访问的

局部变量:只能在变量特定的作用域内起作用

// 全局变量,定义在函数外部,可以在整个程序中使用
int global = 10;

// 函数定义
void myFunction() 
{
    // 局部变量,定义在函数内部,只能在函数内部使用
    int local = 20;

    // 访问全局变量和局部变量,并打印它们的值
    printf("Global: %d\n", global);
    printf("Local: %d\n", local);
}

int main() 
{
    // 调用函数
    myFunction();
    // 尝试访问局部变量,会导致编译错误
    // printf("Local in main: %d\n", local);
    
    // 访问全局变量
    printf("Global in main: %d\n", global);
    return 0;
}

sizeof是函数吗?关键字!!!

说来惭愧,当听到问sizeof是函数吗?第一时间就想到它后面接的是(),理所当然的认为sizeof就是函数…

可是sizeof也可以不加()使用啊

int main()
{
	int val = 100;
	printf("sizeof() : %d\nsizeof : %d\n", sizeof(val), sizeof val);
	return 0;
}

在这里插入图片描述

但是!!!sizeof 在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。

在这里插入图片描述

signed、unsigned 关键字

直接上代码

int main()
{
    char a[1000];
    int i;
    for(i=0; i<1000; i++)
    {
    	a[i] = -1-i;
    }
    printf("%d",strlen(a));
    return 0;
}

乍一看,很简单,再一看,嘶~~~好像要思考一下,看到负数就想到负数在计算机中的存储形式,以补码的形式存储,最高位符号位为1。char类型占1字节即8比特位,所以是从-128~127一共256个数,但是strlen是以\0为结尾,所以一共255个数。

正数的原码反码补码都是一样,没什么好说,而负数的存储是以补码的形式存储,所以负数的存储首先就需要将源码转换为补码,然后在将其存入到内存中。注意!!!!就是这么一个过程,先转换,然后存入。所以我数据的存储是不关注你存放在哪里,存放好之后我能够读取出来就行,所以signed和unsigned两种类型的区别就是我是否关注符号位,然后进行不同的读取。

正数负数我该怎么存就怎么存,有无符号是你读取的方式,读出来多少是你的事

再来一段代码

int i = -20;
unsigned j = 10;

i+j 的值为多少?为什么?

此时的结果随着你读取的方式而变化,如果使用printf(“%d”, i + j)的话,结果为-10。当使用printf(“%u”, i + j)的话,结果为42亿多。

static关键字

static关键字在修饰变量时有两种情况:修饰全局变量,修饰局部变量

修饰全局变量:被修饰的全局变量也称静态全局变量,改变了该全局变量的作用域,使得该变量只在声明它的源文件中可见,而在其他源文件中是不可见的

修饰局部变量:生命周期扩展到整个程序的执行期间,但作用域仍限于声明它的函数内部,整个执行期间只初始化一次,且默认为0

修饰函数:函数的作用域限定在声明它的文件内部,使得该函数对于其他文件是不可见的

void function() 
{
    static int x; // 静态变量
    x++;
    printf("x: %d\n", x);
}

abs函数和fabs函数

abs用于整形的绝对值,fabs用于浮点型的绝对值

空结构体占多大空间?

struct empty
{
    
};

int main()
{
	empty emp;
	printf("empty struct size : %d\n", sizeof(emp));
	return 0;
}

一般而言空结构体的大小是给1字节,但是具体是多少还是依编译器。编译器认为任何一种数据类型都有其大小,用它来定义一个变量能够分配确定大小的空间。(vs中直接报错…)

柔性数组

在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少有一个其他成员,且一个结构体只能有一个柔性数组。

柔性数组在定义时动态开辟空间,不影响结构体的大小

struct array1
{
	int lenth;
	int arr[];
};

struct array2
{
	int lenth;
	int *arr;
};

int main()
{
	struct array1 a;
	struct array2 b;
	printf("size : %d\n", sizeof(a));
	printf("size : %d\n", sizeof(b));
	return 0;
}

在这里插入图片描述

union

union也称联合体或者共用体,顾名思义,就是联合体内所有数据共用一块内存,这块内存大小是成员类型最大的字节数。

可以用union来验证大小端

union U
{
	int a;
	char b;
};

int main()
{

	union U u1;
	u1.a = 1;  //0x0001;
	if (u1.b == 1)
	{
		printf("小端机\n");
	}
	else
	{
		printf("大端机\n");
	}
	return 0;
}

enum

enum枚举类型允许对一批整形变量进行命名,提高代码可读性

不需要实例化对象

enum Weekday {
    Monday = 1,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};

sizeof枚举类型的大小是多少?

enum类型的大小并不是固定的,enum类型的大小是由编译器来决定的,可能是4字节也可能是8字节,并且还与你给出的值有关

enum Week  //demo1
{
  day1 = 0x11223344,
  day2,
  day3
};
// sizeof(enum Week) = 4
enum Week  //demo2
{
  day1 = 0x1122334455,
  day2,
  day3
};
// sizeof(enum Week) = 8

typedef

给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型

注意:typedef在给类型取别名时,完全继承了原始类型的属性,但是不能和其他类型修饰符进行组合使用来修改这些属性

typedef int int32;
int main()
{
    // unsigned int32 a = 0; // 错误
    int32 b = 1;
}    

在这里插入图片描述

取整和取模

整数除法中,C语言的行为规则是向零取整

当求模运算中至少一个操作数为负数时,C语言的求模运算结果的符号由被除数的符号决定。

内存对齐——为什么需要内存对齐?

简要的说,数据按照特定的规则放到对应的地址上就是内存对齐,内存对齐可以增强系统性能,因为对于没有对齐的数据,操作系统读取数据可能需要多次的内存访问,而内存对齐后数据就在对齐边界上,操作系统一次内存访问就可以读取数据,提高性能

在这里插入图片描述

宏定义#define

宏定义的常量或者宏函数都是在预处理阶段直接进行机械替换,因此使用宏函数可以免去函数调用的开销,提高性能。但是定义的这些都没有类型安全检查,并且存在优先级问题,需要谨慎使用

宏的作用域:宏的作用域是在其定义的地方向后

void func1()
{
    int num = M; //不替换
}
int main()
{
    #define M 10
    int num = M; //替换
    func1();
    func2();
    return 0;
}
void func2()
{
    int num = M; // 替换
}

指针和数组的关系?

指针和数组没有关系!!!指针的大小为4/8字节,数组的大小为(类型 * 数据)

指针存放地址,该地址是数据存放的地址。数组存放数据,只是数组名类似于指针,是数组中第一个元素的地址

指针是一个变量,可以指向任何数据类型,而数组是一个固定长度的数据集合

指针可以被重新赋值指向不同的内存地址,而数组名则不能被重新赋值

向特定地址中写入数据?

指针指向某个地址,如果权限允许,我们可以向该地址中写入数据。一般来说关注的都是数据而不是地址,当想向特定地址写入数据的话那该怎么做呢?

假设向0x12ff7c的地址中写入数据

int main()
{
    int *p = (int*)0x12ff7c;
    *p = 10;
    //又或者 *(int*)0x12ff7c = 10;
    return 0;
}

#和##

#号:在宏定义中,#号用于将参数转换为字符串字面值。这个过程称为字符串化。当#号放在宏参数前面时,它将该参数转换为一个以双引号包围的字符串字面值

#define STRINGIZE(x) #x
printf("%s\n", STRINGIZE(hello)); // 将输出 "hello"

##号:在宏定义中,##号用于连接两个标识符或符号

#define CONCAT(x, y) x##y
int ab = 10;
printf("%d\n", CONCAT(a, b)); // 将输出 10

##号只能用于连接标识符或符号,不能用于连接字符串或数字

assert是宏而不是函数

    _ACRTIMP void __cdecl _wassert(
        _In_z_ wchar_t const* _Message,
        _In_z_ wchar_t const* _File,
        _In_   unsigned       _Line
        );

    #define assert(expression) (void)(                                                       \
            (!!(expression)) ||                                                              \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
        )

assert只存在于debug版本中,不存在release版本中,assert的作用是定位错误,而不是排除错误

malloc申请0字节空间

申请0字节函数返回的是正常地址,因为函数的返回值规定了返回NULL代表申请失败,而申请0字节是成功的,但是返回的地址是不可以使用的,强制使用会导致未定义行为

malloc除了给到你申请的空间外,还会额外给你更多的空间来存放元信息

函数参数的传递发生在函数调用之前

int addNum(int num1, int num2)
{
	return num1 + num2;
}

int main()
{
	int ret = addNum(1, 2, 3, 4);
	printf("ret = %d\n", ret);
	return 0;
}

在这里插入图片描述

在C语言中,如果函数没有参数,那么对该函数进行传参也是可以,因为参数传递发生在函数调用之前

void Empty()
{}

int main()
{
	Empty(1, 2, "123");
	return 0;
}

可变参数列表

使用C语言的可变参数列表需要包含<stdarg.h>这个头文件,里面包含有几个宏,例如va_start、va_arg、va_end和va_copy,大致的使用流程:

#include <stdio.h>
#include <stdarg.h>

Myadd(int n, ...)
{
	va_list args; //1. 定义va_list类型变量
	va_start(args, n); //2. 初始化args变量
	int sum = 0;
	for (int i = 0; i < n; i++)
	{
		sum += va_arg(args, int); //去除参数
	}
	va_end(args); //args置空
	return sum;
}

int main()
{
	int ret1 = Myadd(4, 1, 2, 3, 4);
	int ret2 = Myadd(3, 2, 3, 4);
	printf("ret1=%d   ret2=%d\n", ret1, ret2);
	return 0;
}

其中va_list是typedef的,原型为char*

在这里插入图片描述

其他几个都是宏函数

在这里插入图片描述

    //这个宏通常用于确定参数在堆栈上的对齐方式。	
	#define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 

	#define _ADDRESSOF(v)           (&(v))
    #define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
    #define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
    #define __crt_va_end(ap)        ((void)(ap = (va_list)0))

在这里插入图片描述

    //这个宏通常用于确定参数在堆栈上的对齐方式。	
	#define _INTSIZEOF(n)          ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) 

	#define _ADDRESSOF(v)           (&(v))
    #define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))
    #define __crt_va_arg(ap, t)     (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
    #define __crt_va_end(ap)        ((void)(ap = (va_list)0))

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565842.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据库(数据库连接池)

文章目录 1.批处理应用1.基本介绍2.批处理演示1.创建测试表2.修改url3.编写java代码 3.批处理源码分析 2.数据库连接池1.传统连接弊端分析2.数据库连接池基本介绍1.概念介绍2.数据库连接池示意图3.数据库连接池种类 3.C3P0连接池1.环境配置1.导入jar包2.将整个lib添加到项目中3…

云存储属性级用户撤销可追溯的密文策略属性加密方案论文阅读

参考文献为2018年发表的Traceable ciphertext-policy attribute-based encryption scheme with attribute level user revocation for cloud storage 贡献 本篇路提出了一个可追踪、实现属性级用户撤销&#xff08;删除用户的某一属性&#xff09;、支持密钥更新和密文更新、外…

图片二维码如何制作生成?常规图片格式的二维码制作技巧

图片是展示信息很常用的一种方式&#xff0c;而现在查看图片很多人会通过二维码的形式来展现&#xff0c;这种方式优势在于更加的灵活&#xff0c;能够通过一个二维码展示大量的图片内容。那么图片二维码是如何制作生成的呢&#xff1f; 想要快速的将图片转二维码使用&#xf…

mysql-FIND_IN_SET包含查询

如图所示&#xff0c;需要查询字段ancestorid中包含14的所有数据&#xff0c;使用FIND_IN_SET即可实现&#xff0c;不需要使用模糊查找like 示例sql&#xff1a; SELECT * FROM mt_fire_template WHERE FIND_IN_SET(14,ancestorid) 结果

【pysurvival Python 安装失败】

这个错误与 sklearn 包的名称更改有关&#xff0c;导致 pysurvival 在构建元数据时失败。现在&#xff0c;你需要修改 pysurvival 的安装文件以使用正确的 scikit-learn 包名 编辑安装文件&#xff1a;找到 pysurvival 的安装文件&#xff0c;可能是 setup.py 或 pyproject.to…

一文解析智慧城市,人工智能技术将成“智”理主要手段

长期以来&#xff0c;有关智慧城市的讨论主要围绕在技术进步方面&#xff0c;如自动化、人工智能、数据的公开以及将更多的传感器嵌入城市以使其更加智能化。实际上&#xff0c;智慧城市是一个关于未来的设想&#xff0c;其重要原因在于城市中存在各种基础设施、政治、地理、财…

华为OD面试手撕算法-合并排序数组

题目描述 本题是leetcode一道简单题&#xff1a;合并两个有序数组&#xff0c;但是对于时间和空间复杂度面试官明确给出了限制。 // 给定两个排序后的数组 A 和 B&#xff0c;其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法&#xff0c;将 B 合并入 A 并排序。 // 初始化…

【nc工具信息传输】

nc&#xff0c;全名叫 netcat&#xff0c;它可以用来完成很多的网络功能&#xff0c;譬如端口扫描、建立TCP/UDP连接&#xff0c;数据传输、网络调试等等&#xff0c;因此&#xff0c;它也常被称为网络工具的 瑞士军刀 。 nc [-46DdhklnrStUuvzC] [-i interval] [-p source_po…

“多组数组”题的注意事项,天杀的“鲁棒性”

【题目描述】 输入一些整数&#xff0c;求出它们的最小值、最大值和平均值&#xff08;保留3位小数&#xff09;。输入保证这些数都是不超过1000的整数。 输入包含多组数据&#xff0c;每组数据第一行是整数个数n&#xff0c;第二行是n个整数。n&#xff1d;0为输入结束标记&…

高分顶刊,仅2个月录用,13天见刊!另有CCF仅36天录用!

1区人工智能类SCIE &#xff08;高质量&#xff0c;进展顺&#xff09; 【期刊简介】IF&#xff1a;6.0-7.0&#xff0c;JCR1区&#xff0c;中科院2区 【检索情况】SCIE在检 【征稿领域】机器学习、云计算、边缘计算相关均可 【案例分享】2023.10.31 提交——2024.01.31 录…

ES6学习(四)-- Reflect / Promise / Generator 函数 / Class

文章目录 1. Reflect1.1 代替Object 的某些方法1.2 修改某些Object 方法返回结果1.3 命令式变为函数行为1.4 ! 配合Proxy 2. ! Promise2.1 回调地狱2.2 Promise 使用2.3 Promise 对象的状态2.4 解决回调地狱的方法2.5 Promise.all2.6 Promise.race 3. Generator 函数3.1 基本语…

计算机网络|谢希仁版|数据链路层

数据链路层 数据链路层研究的是什么&#xff1f;数据链路层的几个共同问题数据链路与链路帧通信规程 三个基本问题封装成帧透明传输差错检测可靠传输 点对点协议PPPPPP协议应满足的需求PPP协议的组成PPP协议帧的格式各字段的意义字节填充零比特填充PPP协议的工作状态 使用广播信…

How to install JDK on mac

文章目录 1. Install JDK on mac2. zshenv, zshrc, zprofile3. 查看java环境变量配置 1. Install JDK on mac Installation of the JDK on macOS 2. zshenv, zshrc, zprofile How Do Zsh Configuration Files Work? 3. 查看java环境变量配置 open Terminal&#xff0c;cd…

C++ AVL树(旋转)

我们之前学习了搜索二叉树&#xff0c;我们知道普通的搜索二叉树会有特殊情况出现使得二叉树的两枝极其不平衡形成我们通俗说的歪脖子树&#xff1a; 这样的树一定会使得我们的增删查的效率变低&#xff1b;为了避免这种极端的情况出现&#xff0c;在1962年有两位伟大的俄罗斯数…

集成电路企业tapeout,如何保证机台数据准确、完整、高效地采集?

Tapeout即流片&#xff0c;集成电路行业中将CDS最终版电路图提交给半导体制造厂商进行物理生产的过程。在芯片设计与制造的流程中&#xff0c;Tapeout是非常重要的阶段&#xff0c;包括了布局&#xff08;Layout&#xff09;、连线&#xff08;Routing&#xff09;、分析&#…

阿里云服务器租用费用价格表_一年和1个月收费标准(2024版)

2024年阿里云服务器租用费用&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元&#xff0c;ECS u1服务器2核4G5M固定带宽199元一年&#xff0c;2核4G4M带宽轻量服务器一年165元12个月&#xff0c;2核4G服务…

【论文速读】| MASTERKEY:大语言模型聊天机器人的自动化越狱

本次分享论文为&#xff1a;MASTERKEY: Automated Jailbreaking of Large Language Model Chatbots 基本信息 原文作者&#xff1a;Gelei Deng, Yi Liu, Yuekang Li, Kailong Wang, Ying Zhang, Zefeng Li, Haoyu Wang, Tianwei Zhang, Yang Liu 作者单位&#xff1a;南洋理工…

记录一次官网访问很慢的情况

客户查看云监控,带宽未超限,客户取的是1分钟的原生值,也就是1分钟也是个平均值。 但是客户的原始值&#xff0c;其实就是1分钟内的平均值。所以客户的瞬时超限&#xff0c;其实是看不出来的。但是后端同事从实时监控里面可以看到超限的情况。 客户升带宽后&#xff0c; 发现还…

超详细工具Navicat安装教程

Navicat是一款功能强大的数据库管理工具&#xff0c;可用于管理多种类型的数据库&#xff0c;包括MySQL、MariaDB、SQL Server、SQLite、Oracle和PostgreSQL等。以下是Navicat工具的一些主要特点和功能&#xff1a; 一.功能介绍 跨平台支持 多种数据库支持 直观的用户界面 数据…

【Qt 学习笔记】输入框实现helloworld | QLineEdit的使用

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 输入框实现helloworld | QLineEdit的使用 文章编号&#xff1a;Qt 学习…