char, short, int, long, float, double, struct, union, enum, void, const, signed, unsigned, sizeof, typedef, auto, static, register, extern, if, else, switch, case, default, for, while, do, break, continue, return
char, short, int, long, long long, float, double, void, struct, union, enum
等。unsigned
表示无符号类型,最高位不作为符号位,而是数值位。int
,小数默认是double
。所以定义long
类型时最好在数字末尾加l, L
,定义float
类型最好在数字末尾加f, F
基本数据类型 | 字节数 | 范围 |
---|---|---|
char | 1 | -128~127 |
short | 2 | -32768~32767 |
int | 2 或 4 | -231~231-1 |
long | 4 | -231~231-1 |
long long | 8 | -264~264-1 |
float | 4 | -- |
double | 8 | -- |
变量除了有数据类型,还有另外一个重要属性,存储类型。数据类型决定了变量的大小,而存储类型决定了变量的生命周期和可见性。
C 语言中,变量的存储类型有 4 种,auto, static, register, extern
,一般使用的只有前两个。
auto
,默认存储类型。auto 只能用在函数内,修饰局部变量。static
,全局变量默认以 static 修饰。当修饰局部变量时,必须且只能被初始化一次。register
,用于定义存储在寄存器中而不是 RAM 中的局部变量,用于运算要求高的变量。extern
,用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。+, -, *, /, %, ++, --, <, <=, >, >=, ==, !=, &&, ||, !, &, |, ~, ^, >>, <<, +=, sizeof, ., [], *, (), ?:, 强制类型转换
a^b=c, 则a^c=b, b^c=a
,这一规律广泛运用于密码学。a>>n,相当于a/(2^n)
a<<n,相当于a*2^n
,最低位直接补 0。int a[10]={1, 2, 3}
,前 4 个数分别为 1, 2, 3,后面的数均初始化为 0。\0
,占一个字节。变量都存放在从某个内存地址开始的连续的若干个字节中。
int *p = (int *)1000
,指针指向内存地址为 1000 的位置,管辖后面连续的 4 个字节。指针变量是一种数据类型,它的大小均为sizeof(T *) = 4
,故无论指针变量指向的是何种数据类型,它的大小均为 4 字节。
这是因为当今主流的 CPU 为 32 位,寻址范围是 2^32=4G,所以指针变量只需要 32 位即可。当然对于 64 位 CPU,编译器产生的指针长度也就是sizeof(T *) = 8
。
例如,将0x1234bacd
以不同的方式存储:
地址 | 大端 | 小端 |
---|---|---|
0x0000 | 12 | cd |
0x0001 | 34 | ba |
0x0002 | ab | 34 |
0x0003 | cd | 12 |
unsigned short a = 0x1234;
char *p = (char *)&a;
for(int i=0;i<2;i++){
printf("%2x\n", *(p+i));
}
如果
short a = 0xf234
,在输出首字节的0xf2
时,会出现fffffff2
而不是f2
,猜测可能是由于printf()
函数在输出时将char
自动转换成了 4 字节的int
造成的,因为%x
对应的就是整型,整型默认是int
。
p2-p1 = (p2-p1)/sizeof(T)
p ± n = p ± n*sizeof(T)
char *p = (char *)malloc(200 * sizeof(char))
free(p)
p = NULL
int **p
const int *p = &a
int * const p = &a
int add(int a, int b)
{
return a+b;
}
int main()
{
int (*p)(int, int); // 定义了一个指向某一类型的函数指针p
p = add;
cout << p(1, 2) << endl;
}
stuct student
{
string name;
int age;
};
// 初始化
student s1;
student s2 = {"Tom", 18};
cout << s2.name << endl; // 变量
student s3 = {
name : "Alice",
age : 16,
};
student *p = &s3;
cout << p->name << endl; // 指针
union data
{
int a;
float b;
char c[20];
}
联合体的大小以最大的类型为主,也就是说上面定义的data
联合体的大小为 20 字节。
联合体在同一时间只使用一个变量,因为他们共用一块最大的内存。
union data data1;
data1.i = 10;
data1.f = 220.5; // 此时会覆盖掉数据 10
strcpy( data1.str, "C Programming"); // 此时会覆盖掉数据 220.5
printf( "data.str : %s\n", data1.str);
// 第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1
enum DAY
{
SUN, MON, TUE, WED, THU, FRI, SAT
};
// 可以为单独某个值指定数值,后续枚举成员的值仍在这个基础上加 1
// 如下,spring = 0, summer = 3, autumn = 4, winter = 5
enum season {spring, summer=3, autumn, winter};
#include <stdio.h>
#include "mymath.h"
#define, #undef, #ifdef, #ifndef, #if, #else, #elif, #endif
#define add(a, b) a+b
如果一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种情况,标准的做法是把文件的整个内容放在条件编译语句中,一般使用#idndef xx
来解决这一问题,放在头文件中。
#ifndef _MYMATH_H
#define _MYMATH_H
// xxx
#endif
extern "C"
的主要作用就是为了能够正确实现 C++ 代码调用其他 C 语言代码。加上extern "C"
后,会指示编译器这部分代码按 C 语言(而不是 C++)的方式进行编译。
由于 C 和 C++ 编译器(对应 gcc 和 g++)对函数的编译处理是不完全相同的,尤其对于 C++ 来说,支持函数的重载,编译后的函数一般是以函数名和形参类型来命名的。例如函数void fun(int, int)
,编译后的可能是_fun_int_int
(不同编译器可能不同,但都采用了类似的机制,用函数名和参数类型来命名编译后的函数名)。而 C 语言没有类似的重载机制,一般是利用函数名来指明编译后的函数名的,对应上面的函数可能会是_fun
这样的名字。
// 模块 A 头文件 include/moduleA.h
#idndef _MODULE_A_H
#define _MODULE_A_H
int foo(int x, int y);
#endif
// 模块 A 实现文件 include/moduleA.c
#include "moduleA.h"
int foo(int x, int y)
{
return x + y;
}
// 主函数 src/main.cpp
#include <bits/stdc++.h>
#include "moduleA.h"
using namespace std;
int main()
{
printf("%d\n", foo(1, 2));
return 0;
}
此时,如果模块 A 的实现文件采用 C 编译,而模块 B 采用 C++ 编译,那么在链接阶段,链接器会从模块 A 生成的目标文件 moduleA.obj 中找_foo_int_int
这样的符号,显然这是不可能找到的,因为foo()
函数被编译成了_foo
的符号,因此会出现链接错误。
gcc -c moduleA.c && cd ../src
g++ -I ../include main.cpp ../include/moduleA.o
# 报错
/bin/ld: /tmp/cc6vh97T.o: in function `main‘:
main.cpp:(.text+0x13): undefined reference to `foo(int, int)‘
collect2: error: ld returned 1 exit status
如果在主函数文件中,加入extern "C"
,对引用头文件部分指明用 C 语言编译,即可正确调用。
#include <bits/stdc++.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "moduleA.h" // C 编译
#ifdef __cplusplus
}
#endif
using namespace std;
int main()
{
printf("%d\n", foo(1, 2));
return 0;
}
这个功能十分有用处,因为在 C++ 出现以前,很多代码都是 C 语言写的,而且很底层的库也是 C 语言写的,为了更好的支持原来的 C 代码和已经写好的 C 语言库,需要在 C++ 中尽可能的支持 C,而extern "C"
就是其中的一个策略。这个功能主要用在下面的情况:
#ifndef __INCvxWorksh // 防止该头文件被重复引用
#define __INCvxWorksh
#ifdef __cplusplus // 告诉编译器,如果定义了 __cplusplus(即如果是cpp文件),那么这部分代码按 C 语言的格式进行编译,而不是 C++
extern "C"{
#endif
// 这里可以是复合语句
// 也可以是头文件,相当于头文件中的声明都加了 extern "C"
/*...*/
#ifdef __cplusplus
}
#endif
/*...*/
#endif // end of __INCvxWorksh
printf()
,标准输出到stdout, stderr
scanf()
,标准输入到stdin
int fgetc(FILE *)
,返回输入的一个字符,EOF
时返回-1。int fputc(int c, FILE *)
,输出一个字符。char *fgets(char *s, int n, FILE *)
,输入一段文本以回车结束。int fputs(const char *s, FILE *)
,输出一段文本。sscanf(buf, "%d%d", &a, &b)
,从数组中读。sprintf(buf, "%d%d", a, b)
,写入数组。fscanf(FILE *, 其他)
,从文件读。fprintf(FILE *, 其他)
,写入文件。freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
FILE *fopen(const char * filename, const char * mode);
,其中,filename
是文件路径的字符串,mode
是打开模式,如下:
模式 | 含义 |
---|---|
r | 只读 |
w | 覆盖写,若文件不存在则创建 |
a | 追加写,若文件不存在则创建 |
xb | 操作的是二进制 |
x+ | 允许读写 |
int fputc( int c, FILE *fp );
,返回输出的字符,否则 EOF。int fputs( const char *s, FILE *fp );
,成功返回一个非负值,否则 EOF。int fgetc( FILE * fp );
,返回读取的字符,否则 EOF。char *fgets( char *buf, int n, FILE *fp );
fscanf(FILE *, 其他)
,从文件读。fprintf(FILE *, 其他)
,写入文件。size_t fread(void *buf, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
,返回已读取字节数。size_t fwrite(const void *buf, size_t size_of_elements, size_t number_of_elements, FILE *a_file);
,返回已写入字节数。int fseek(FILE *stream, long offset, int whence);
,移动文件指针到指定位置。
offset
是相对于 whence 的偏移位置,正数往后,负数往前。whence
可以是SEEK_SET, SEEK_CUR, SEEK_END
,分别是文件头,当前位置,文件尾。int ftell(FILE *stream)
,返回当前文件指针位置距离文件头的字节数。通过上面两个函数可以获取一个文件的大小:
FILE *fp = fopen("in.txt", "r");
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
fseek(fp, 0, SEEK_SET);
int fclose( FILE *fp );
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
time_t time(time_t *t)
,返回自元时间以来经过的秒数,该秒数会同时返回给参数和返回值。struct tm *localtime(const time_t *t)
,返回当地时区时间。struct tm *gmtime(const time_t *t)
,返回用标准格林尼治时区(GMT)表示。time_t mktime(stuct tm *time)
,将结构体时间转化为秒数。double difftime(time_t t2, time_t t1)
,返回 t2-t1 的差值。size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
,根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。clock_t clock(void)
,返回处理器时钟所以用时间,用于得到程序使用时间。// ./a.ot hahaha
int main(int argc, char *argv[])
{
printf("可执行程序 %s,参数个数为[%d],运行输出:[%s]\n", argv[0], argc, argv[1]);
return 0;
}
printf("\r%d", count++);
// printf("\b\b%d", count++);
#define PI 3.141592654
const double PI = 3.1415926;
bool, namespace, using, class, private, public, protected, this, super, template, new, delete, inline, virtual, true, false
引用变量是一个别名,引用是一种安全指针的机制。T & a = b;
int a = 5;
int &b = a; // b 为 a 的引用
int swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int a = 1, b= 2;
swap(a, b);
C++ 程序中的内存分为两个部分:
很多时候,我们无法提前预知需要多少内存来存储某个定义变量中的特定信息,所需内存的大小需要在运行时才能确定。在 C++ 中,就可以使用 new 运算符为给定类型的变量在运行时分配堆内的内存,它会返回所分配的空间地址。
int *p = new int, int *p = new int[10]
delete p, delete[] p
在 CPP 中,函数的形参可以指定默认值,并且拥有默认值的参数必须放在最后面。
若定义的函数有前向引用申明,那么默认参数值必须在声明中定义,在实现的时候不可以再次指定默认值。
函数名相同,函数的参数个数或者参数类型不同,则称之为函数重载。
函数的返回值类型不可以作为判断函数重载的依据。
定义函数时在返回类型前面添加关键字inline
,在编译时会用在调用函数的位置展开函数体,以减少参数传递,控制转移等的开销。
内联函数体内不能有循环和 switch 等语句,函数体必须简单。
try {
// 保护代码
} catch (ExceptionName e1) {
// catch 块
cout << e1.what() << endl;
} catch (ExceptionName e2) {
// catch 块
} catch (ExceptionName eN) {
// catch 块
} catch (...) {
// 处理任何异常
}
try {
if (str == "int") throw 100;
else throw "ahhaha";
} catch(const int a) {
cout << a << endl;
} catch(const string b) {
cout << b << endl;
} catch( ... ) {
cout << "other exception" << endl;
}
#include <iostream>
#include <thread>
using namespace std;
void fun(int num)
{
while(true){
cout << num << endl;
}
}
int main()
{
thread t1(fun, 1);
thread t2(fun, 2);
// t1.join(),主线程会阻塞在这里
// t1.detach(),线程会后台运行,无需阻塞等待
// 主线程不会等待子线程结束
t1.join();
t2.join();
}
原文:https://www.cnblogs.com/zghong/p/14149140.html