一、基础常识
1.基本数据类型
bool:1字节
char:1字节
int: 4字节
float: 4字节
double:8字节
#include <iostream>
using namespace std;
int main(){
bool flag; //1
char ch; //1
int integer;//4
float fl; //4
double dl; //8
int len1 = sizeof(flag);
int len2 = sizeof(ch);
int len3 = sizeof(integer);
int len4 = sizeof(fl);
int len5 = sizeof(dl);
cout<<len1<<endl<<len2<<endl<<len3<<endl<<len4<<endl<<len5<<endl;
}
输出:
2.一个函数的组成
一个完整的函数包括:
返回类型 函数名(参数列表)
:初始化列表
{
函数体;
return 返回值;
}
3.十六进制:0x
0x11
是十六进制数
十六进制数 | 十进制数 | 二进制数 |
---|---|---|
0x11 | 17 | 0001 0001 |
4.C语言的头文件
头文件 | 函数 | 作用 |
---|---|---|
#include <stdio.h> | printf、scanf | 定义输入/输出函数 |
#include <string.h> | strcpy | 字符串处理 |
#include <stdlib.h> | malloc、free | 定义杂项函数及内存分配函数 |
5.字符常量、转义序列
1.字符常量
单引号括起来的是字符常量
双引号括起来的是字符串
2.转义序列
\
后加1-3个八进制数 或 \x
后加十六进制数,可转义为对应的ASCII码值
'\x32’是转16进制,代表ASCII码值为50
例题1:
6.ASCII码表
7.正斜杠与反斜杠
正斜杠 /
反斜杠 \
8.if就近原则
9.表达式
(1)赋值表达式
#include <stdio.h>
int main(){
int a = 0, x = 1,y = 2;
a = (x = y);
printf("%d",a); //输出结果为2
return 0;
}
分析:while(0),不执行循环体
(2)逻辑表达式的短路运算
跳转链接:https://blog.csdn.net/Edward1027/article/details/131363511
10.return语句
11.for循环
for(单次表达式;条件表达式;末尾循环体){
中间循环体;
}
二、运算符
1.自增运算符:i++ 与 ++i
结论:++i效率更高
2.取地址:&、取内容:*
3.三元表达式 / 三目运算符
1.条件表达式:要进行判断的表达式?真:假
2.举例:返回 a和b中较大的一个值:
(1)传统方法:if else实现
if(a>b) return a;
else return b;
(2)条件运算符(三目运算符):
return (a>b)?a:b;
三、常用函数
1.scanf、printf 、fgets
(1)scanf:输入数据
scanf("%d",&a);
%d:Decimal(十进制)
%c:Char(字符)
%s:String(字符串)
%f:Float(浮点数)
%u:Unsigned int(无符号整数)
%p:Pointer(地址/指针)
例题1:
A.应该是%lf
B.应该是x+6
C.p+6正确,选C
D.p是指针,不是数组
(2)printf:输出数据
格式控制
%d:Decimal(十进制)
%c:Char(字符)
%s:String(字符串)
%f:Float(浮点数)
%u:Unsigned int(无符号整数)
%p:Pointer(地址/指针)
%-10.2f
①%
表示开始格式说明符。
②-
表示左对齐输出。如果没有这个符号,默认是右对齐。
③10
表示输出的宽度。在这个例子中,输出的字符数至少是 10 个。如果数字本身不够长,将会根据对齐方式添加空格。
④.2
表示浮点数格式,小数点后保留两位小数。
#include <stdio.h>
int main()
{
// char a = "张三";//错误,char只能保存1B。保存字符串应该用char数组
char a = 'a';
printf("%c 的ASCII码为 %d\n",a,a);
char b[4] = {"张"}; //字符数组,用来保存汉字,一个汉字2B
printf("%s",b);
printf("\n第一天\n");
return 0;
}
拓展:对比一下C++的cout
C:输出 张三 👇
#include <stdio.h>
int main()
{
char a[]= "??";
printf("%s\n",a);
char b[]="张三";
printf("%s\n",b);
}
C++:输出 张三 👇
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
string a = "张三";
cout<<a<<endl;
}
显然C++的cout要好用很多,不需要像C要记住printf的参数。
(3)fgets:读取一行内容
C语言读取一行,fgets
fgets(buf,n,stdin)
拓展:
C++读取一行,cin.getline
cin.getline(a,5);
(4)getchar()
2.sizeof( )
sizeof 运算符用于计算一个对象或类型所占的内存字节数
(1)字符串
C语言字符串是以\0
结尾
int a = sizeof("Hello"); //6
int b = strlen("Hello"); //5
(2)指针
64位系统中,指针的大小是8字节。
void getArray(char a[10]){
printf("%d", sizeof(a)); //8
}
这段代码中,sizeof(a)的值将是指针的大小,而不是数组a的实际大小。这是因为在getArray函数参数中,char a[10]实际上被当作char* a处理。当数组作为参数传递给函数时,它会退化为指向数组第一个元素的指针。因此,sizeof(a)在getArray函数中给出的是指针的大小,而不是数组的大小。
四、C风格字符串
1.以\0
结尾
'\0'
称为 字符串结束符,英文称为 NULL,中文称为 空字符
C语言以'\0'
作为字符串结尾,占一个位。
注意:在定义字符串数组时,应该为’\0’预留空间,否则在使用字符串时可能会发生数组越界的问题
例题1:指出程序中的错误, 并改正程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main(){
int len = strlen("hello");
char *str = malloc(len*sizeof(char));
strcpy(str, "hello");
printf("%s\n", str);
return;
}
答案:
①main函数的返回值类型是int,最后是return 0
②C字符串以\0
结尾,要预留一个字符的空间。
③malloc默认是void *
,必须强转
修改:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
int len = strlen("hello");
char *str = (char *)malloc((len+1)*sizeof(char)); //len+1
strcpy(str, "hello");
printf("%s\n", str);
return 0;
}
2.char c[ ] 与 char *c
char c[ ] 是 字符数组,此时c也可以当作char型指针
char *c 只能是一个char型指针
例题1:请说明此程序在做什么操作,并指出程序中的错误, 并改正程序
#include <stdio.h>
void main(){
char *temp1, *temp2;
char *input = "hello,world";
for(int i = 0; i < strlen (input); i++ ) {
if( input[i] == ',') {
input[i] = '\0';
temp1 = input;
temp2 = input[i+1];
}
}
printf("%s\n", temp1);
printf("%s\n", temp2);
return;
}
答:
该程序期望以逗号为分隔符,将hello和world分别输出。
有以下错误:
①int main
②使用strlen,未加头文件
③char *input = "hello,world";
,这样字符串就存储到了只读段(代码段),不可被修改。而input是char型指针,指向该只读段。
④temp1、temp2都是指针类型,temp2 = input[i+1];
应改为temp2 = &input[i+1];
修改:
#include <stdio.h>
#include <string.h>
int main(){
char *temp1, *temp2;
char input[] = "hello,world";
for(int i = 0; i < strlen (input); i++ ) {
if( input[i] == ',') {
input[i] = '\0';
temp1 = input;
temp2 = &input[i+1];
}
}
printf("%s\n", temp1);
printf("%s\n", temp2);
return 0;
}
例题2:指出程序中的多处错误, 并改正程序
要求在GetMem中分配内存,改动请保持GetMen接口不变
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMem(char **p){
*p=(char *)malloc(100);
}
int main(){
char **str;
GetMem(str);
strcpy(*str, "hello world");
printf("%s", *str);
return 0;
}
修改:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMem(char **p){
*p = (char *)malloc(100);
}
int main(){
char *str;
GetMem(&str);
strcpy(str, "hello world");
printf("%s", str);
return 0;
}
3.C风格字符串的初始化、strcpy
数组声明时可初始化,若在数组声明时没有初始化,则只能一个一个赋值,不能整体赋值。可使用strcpy();
4.strlen
strlen()时,只计算纯粹的C风格字符串的长度,不考虑\0的长度
#include <stdio.h>
#include <string.h>
int main(){
char str[15] = "hello!";
printf("%d\n",strlen(str));
return 0;
}
五、宏
1.防止头文件重复包含
若头文件为computer.h,则可以加宏命令,防止头文件重复包含。
【详见条件编译:C++ (week4):Linux基础】
#ifdef __Computer_H__
#define __Computer_H__
/* 头文件内容 */
#endif //__Computer_H__
#pragma once
/* 头文件内容 */
原理:
#ifndef:检查宏是否未定义。
#define:定义宏。
#endif:结束条件编译。
(1)第一次包含头文件时:
预处理器遇到#ifndef __TASK_H__
,检查宏__TASK_H__
是否未定义。因为这是第一次包含,这个宏还没有定义。
由于__TASK_H__
未定义,条件为真,继续执行后续代码。
预处理器遇到#define __TASK_H__
,定义宏__TASK_H__
。
从而,整个头文件的内容会被包含到编译单元中。
(2)第二次及后续包含头文件时:
预处理器遇到#ifndef __TASK_H__
,检查宏__TASK_H__
是否未定义。由于在第一次包含时已经定义了这个宏,所以条件为假。
由于__TASK_H__
已经定义,预处理器跳过#define __TASK_H__
和#endif
之间的所有代码。
因此,头文件的内容不会再次被包含,避免了重复定义。
(3)#endif
对应 #if
、#ifdef
、#ifndef
作为开头
头文件包含是一个递归(循环)的过程,如果被包含的头文件中还包含了其他的头文件,预处理器会继续将它们也包含进来;这个过程会一直持续下去,直到不再包含任何头文件,这与递归的过程颇为相似。
递归包含会导致一个问题,就是重复引入同一个源文件,如果不做任何处理,不仅会出现重复定义错误,而且不符合编程规范。
对此,可以通过宏保护来解决这个问题。
例题1:有一个头文件pfucert.h, 头文件被下面的宏包围,作用是什么。
#ifndef __PFUCERT_H__
#define __PFUCERT_H__
/* 头文件内容 */
#endif // _PFUCERT_H_
答:防止头文件被重复包含
2.三种预处理命令:include、宏定义、宏函数
#:预处理指令
① #include :头文件复制过来
②宏定义: 是 文本替换
③宏函数:参数要括起来,整个表达式也要括起来。左括号紧贴宏函数名,否则会当成宏定义
为什么要用宏函数?
①宏函数快,仅仅是替换,避免了函数调用开销。
应用场景:简短的、频繁调用的函数,写成宏函数,可以降低函数调用开销
②提供了一定的宏编程能力