unsigned:声明无符号类型变量或函数 signed:声明有符号类型变量或函数 无符号数只限于char int long short类型 float double类型没有无符号数 符号特指负号“-”
//获得无符号基本数据类型的空间大小 #include <stdio.h> int main() { unsigned char character_type; unsigned short short_integer; unsigned int integer; unsigned long long_integer; //unsigned float floating_point_type; //unsigned double double_float; printf("character_type:%d\n",sizeof(character_type)); printf("short_integer:%d\n",sizeof(short_integer)); printf("integer:%d\n",sizeof(integer)); printf("long_integer:%d\n",sizeof(long_integer)); //printf("floating_point_type:%d\n",sizeof(floating_point_type)); //printf("double_float:%d\n",sizeof(double_float)); }计算数据类型长度,sizeof 是关键字不是函数 代码见上文
用于声明结构体变量或函数;将一些相关联的数据打包成一个整体
#include <stdio.h> int main() { struct student { }stu; printf("%d",sizeof(stu)); } //sizeof(stu)的值是多少呢?声明共用数据类型 union 关键字的用法与 struct 的用法非常类似。 struct中的每个域在内存中都独立分配空间 union只分配最大域的空间,所有域共享这个空间
#include <stdio.h> int main() { union StateMachine { char character; int number; char *str; //double exp; }stu; printf("%d\n", sizeof(stu)); }声明枚举类型 成员都是常量,也就是我们平时所说的枚举常量(常量一般用大写)。 enum 变量类型还可以给其中的常量符号赋值,如果不赋值则会从被赋初值的那个常量开始依次加 1,如果都没有赋值,它们的值从 0 开始依次递增 1。
#include <stdio.h> int main() { enum Color { GREEN = 1, RED = 2, BLUE = 3, GREEN_RED = 10, GREEN_BLUE = 11 }ColorVal; ColorVal = GREEN; printf("%d\n", sizeof(ColorVal)); printf("%d\n", ColorVal); }为基本数据类型定义新的类型名 为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称 为数组定义简洁的类型名称 为指针定义简洁的名称
在实际使用中,typedef 的应用主要有如下4种。
1) 为基本数据类型定义新的类型名
也就是说,系统默认的所有基本类型都可以利用 typedef 关键字来重新定义类型名,示例代码如下所示: typedef unsigned int COUNT; 而且,我们还可以使用这种方法来定义与平台无关的类型。比如,要定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型,即: typedef long double REAL; 在不支持 long double 的平台二上,改为: typedef double REAL; 甚至还可以在连 double 都不支持的平台三上,改为: typedef float REAL; 这样,当跨平台移植程序时,我们只需要修改一下 typedef 的定义即可,而不用对其他源代码做任何修改。其实,标准库中广泛地使用了这个技巧,比如 size_t 在 VC++2010 的 crtdefs.h 文件中的定义如下所示:
#ifndef _SIZE_T_DEFINED #ifdef _WIN64 typedef unsigned __int64 size_t; #else typedef _W64 unsigned int size_t; #endif #define _SIZE_T_DEFINED #endif2) 为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称 以结构体为例,下面我们定义一个名为 Point 的结构体:
struct Point { double x; double y; double z; };在调用这个结构体时,我们必须像下面的代码这样来调用这个结构体: struct Point oPoint1={100,100,0}; struct Point oPoint2; 在这里,结构体 struct Point 为新的数据类型,在定义变量的时候均要向上面的调用方法一样有保留字 struct,而不能像 int 和 double 那样直接使用 Point 来定义变量。现在,我们利用 typedef 定义这个结构体,如下面的代码所示:
typedef struct tagPoint { double x; double y; double z; } Point;在上面的代码中,实际上完成了两个操作: 1、定义了一个新的结构类型,代码如下所示:
struct tagPoint { double x; double y; double z; } ;其中,struct 关键字和 tagPoint 一起构成了这个结构类型,无论是否存在 typedef 关键字,这个结构都存在。
2、使用 typedef 为这个新的结构起了一个别名,叫 Point,即: typedef struct tagPoint Point 因此,现在你就可以像 int 和 double 那样直接使用 Point 定义变量,如下面的代码所示:
Point oPoint1={100,100,0}; Point oPoint2;为了加深对 typedef 的理解,我们再来看一个结构体例子,如下面的代码所示:
typedef struct tagNode { char *pItem; pNode pNext; } *pNode;从表面上看,上面的示例代码与前面的定义方法相同,所以应该没有什么问题。但是编译器却报了一个错误,为什么呢?莫非 C 语言不允许在结构中包含指向它自己的指针? 其实问题并非在于 struct 定义的本身,大家应该都知道,C 语言是允许在结构中包含指向它自己的指针的,我们可以在建立链表等数据结构的实现上看到很多这类例子。那问题在哪里呢?其实,根本问题还是在于 typedef 的应用。 在上面的代码中,新结构建立的过程中遇到了 pNext 声明,其类型是 pNode。这里要特别注意的是,pNode 表示的是该结构体的新别名。于是问题出现了,在结构体类型本身还没有建立完成的时候,编译器根本就不认识 pNode,因为这个结构体类型的新别名还不存在,所以自然就会报错。因此,我们要做一些适当的调整,比如将结构体中的 pNext 声明修改成如下方式:
typedef struct tagNode { char *pItem; struct tagNode *pNext; } *pNode;或者将 struct 与 typedef 分开定义,如下面的代码所示:
typedef struct tagNode *pNode; struct tagNode { char *pItem; pNode pNext; };在上面的代码中,我们同样使用 typedef 给一个还未完全声明的类型 tagNode 起了一个新别名。不过,虽然 C 语言编译器完全支持这种做法,但不推荐这样做。建议还是使用如下规范定义方法:
struct tagNode { char *pItem; struct tagNode *pNext; }; typedef struct tagNode *pNode;3) 为数组定义简洁的类型名称 它的定义方法很简单,与为基本数据类型定义新的别名方法一样,示例代码如下所示:
typedef int INT_ARRAY_100[100]; INT_ARRAY_100 arr;4) 为指针定义简洁的名称 对于指针,我们同样可以使用下面的方式来定义一个新的别名: typedef char* PCHAR; PCHAR pa; 对于上面这种简单的变量声明,使用 typedef 来定义一个新的别名或许会感觉意义不大,但在比较复杂的变量声明中,typedef 的优势马上就体现出来了,如下面的示例代码所示: int (a[5])(int,char); 对于上面变量的声明,如果我们使用 typdef 来给它定义一个别名,这会非常有意义,如下面的代码所示: // PFun是我们创建的一个类型别名 typedef int (PFun)(int,char); // 使用定义的新类型来声明对象,等价于int(a[5])(int,char); PFun a[5]; 小心使用 typedef 带来的陷阱 接下来看一个简单的 typedef 使用示例,如下面的代码所示: typedef char PCHAR; int strcmp(const PCHAR,const PCHAR); 在上面的代码中,“const PCHAR” 是否相当于 “const char*” 呢? 答案是否定的,原因很简单,typedef 是用来定义一种类型的新别名的,它不同于宏,不是简单的字符串替换。因此,“const PCHAR”中的 const 给予了整个指针本身常量性,也就是形成了常量指针“charconst(一个指向char的常量指针)”。即它实际上相当于“charconst”,而不是“const char*(指向常量 char 的指针)”。当然,要想让 const PCHAR 相当于 const char* 也很容易,如下面的代码所示: typedef const char* PCHAR; int strcmp(PCHAR, PCHAR); 其实,无论什么时候,只要为指针声明 typedef,那么就应该在最终的 typedef 名称中加一个 const,以使得该指针本身是常量。 还需要特别注意的是,虽然 typedef 并不真正影响对象的存储特性,但在语法上它还是一个存储类的关键字,就像 auto、extern、static 和 register 等关键字一样。因此,像下面这种声明方式是不可行的: typedef static int INT_STATIC; 不可行的原因是不能声明多个存储类关键字,由于 typedef 已经占据了存储类关键字的位置,因此,在 typedef 声明中就不能够再使用 static 或任何其他存储类关键字了。当然,编译器也会报错,如在 VC++2010 中的报错信息为“无法指定多个存储类”。 示例1
#include <stdio.h> typedef char (*PTR_TO_ARR)[30]; typedef int (*PTR_TO_FUNC)(int, int); int max(int a, int b) { return a>b ? a : b; } char str[3][30] = { "http://c.biancheng.net", "C语言中文网", "C-Language" }; int main() { PTR_TO_ARR parr = str; PTR_TO_FUNC pfunc = max; int i; printf("max: %d\n", (*pfunc)(10, 20)); for(i=0; i<3; i++){ printf("str[%d]: %s\n", i, *(parr+i)); } return 0; }示例2:
# include <stdio.h> typedef struct Student { int sid; char name[100]; char sex; }*PST; //PST等价于struct Student * int main( void ) { struct Student st; PST ps = &st; ps->sid = 99; printf( "%d\n", ps->sid ); return 0; }示例3
# include <stdio.h> typedef struct Student { int sid; char name[100]; char sex; }*PSTU, STU;//等价于STU代表struct Student, PSTU代表了struct Student * int main( void ) { STU st;//struct Studtn st; PSTU ps = &st;//struct Student *ps = &st; ps->sid = 99; printf( "%d\n", ps->sid ); return 0; } #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned int u32; //typedef和结构体结合使用 struct MyStruct { int a; int b; }; typedef struct MyStruct2 { int a; int b; }TMP; /* void, 无类型 1、函数参数为空,定义函数时,可以用void修饰: int fun(void) 2、函数没有返回值:void fun(void); 3、不能定义void类型的普通变量: void a; //err,无法确定类型,不同类型分配空间不一样 4、可以定义void *变量: void *p; //ok, 32永远4字节,64永远8字节 5、数据类型本质:固定内存块大小别名 6、void *p万能指针,函数返回值,函数参数 */ int main(void) { u32 t; // unsigned int //定义结构体变量,一定要加上struct关键字 struct MyStruct m1; //MyStruct m2; //err TMP m3; struct MyStruct2 m4; char buf[1024]; strcpy(buf, "1111111111"); void *p; printf("%c",buf[0]); printf("\n"); system("pause"); return 0; }if:条件语句 else :条件语句否定分支(与 if 连用)
switch :用于开关语句 case:开关语句分支 default:开关语句中的“其他”分支
按照百分制考试分数输出等级 #include "stdio.h" main() { int grade; printf("input grade="); scanf("%d",&grade); switch ((int)(grade/10)) { case 10: case 9: printf("A\n"); break; case 8: printf("B\n"); break; case 7: printf("C\n"); break; case 6: printf("D\n"); break; default: printf("E\n"); } }C 语言中循环语句有三种:while 循环、do-while 循环、for 循环。 for:一种循环语句 do :循环语句的循环体 while :循环语句的循环条件
C:while
#include <stdio.h> void main ( ) { int sum=0,i=1; while (i<=100) { sum=sum+i; i++; } printf("sum=%d\n",sum); }C:do…while
#include <stdio.h> void main( ) { int sum=0,i=1; do { sum=sum+i; i++; }while (i<=100); printf("sum=%d\n",sum); }C:for
#include <stdio.h> void main( ) { int sum=0,i; for (i=1;i<=100;i++) sum=sum+i; printf("sum=%d\n",sum); }goto:无条件跳转语句 一般来说,编码的水平与 goto 语句使用的次数成反比。
#include <stdio.h> int main () { /* 局部变量定义 */ int a = 10; /* do 循环执行 */ LOOP:do { if( a == 15) { /* 跳过迭代 */ a = a + 1; goto LOOP; } printf("a 的值: %d\n", a); a++; }while( a < 20 ); return 0; }continue:结束当前循环,开始下一轮循环 break:跳出当前循环
return :子程序返回语句(可以带参数,也可不带参数)循环条件 return 用来终止一个函数并返回其后面跟着的值。
void修饰函数返回值和参数 如果函数没有返回值,那么应该将其声明为void型 如果函数没有参数,应该声明其参数为void void修饰函数返回值和参数仅为了表示无 不存在void变量 C语言没有定义void究竟是多大内存的别名 没有void的标尺,无法在内存中裁剪出void对应的变量 灰色地带 编译器产家自己定义void大小
第一个作用:修饰变量。 变量前加 static 使得函数成为静态变量。 第二个作用:修饰函数。 函数前加 static 使得函数成为静态函数。
//(1)变量不加 static 修饰 #include <stdio.h> void test() { int num = 0; num++; printf("%d ", num); } int main() { int i = 0; for (i = 0; i < 10; i++) { test(); } return 0; } //(2)变量被 static 修饰 #include <stdio.h> void test() { static int num = 0; num++; printf("%d ", num); } int main() { int i = 0; for (i = 0; i < 10; i++) { test(); } return 0; }声明只读变量 在C语言中const修饰的变量是只读的,其本质还是变量 const修饰的变量会在内存占用空间 本质上const只对编译器有用,在运行时无用 修饰一般变量:一般常量是指简单类型的只读变量。这种只读变量在定义时,修饰符 const 可以用在类 型说明符前,也可以用在类型说明符后。 修饰只读变量:定义 const 只读变量,具有不可变性。
#include<stdio.h> int main(void) { const int a; //代表一个常整型数 int const b; //代表一个常整型数 const char *c; // 是一个指向常整型数的指针 // (所指向的内存数据不能修改, // 但是本身可以修改) char * const d; // 常指针(指针变量不能被修改, // 但是它所指向的内存空间可以被修改) const char * const e;// 一个指向常量整型的常指针 // (指针和它所指向的内存空间, // 均不可以修改) return 0; }auto关键字声明一个变量为自动变量,是C语言中应用最广泛的一种类型,在函数内定义变量时,如果没有被声明为其他类型的变量都是自动变量,也就是说,省去类型说明符auto的都是自动变量。
#include <stdio.h> #include <Windows.h> int main() { double value1 = 3.2, value2 = 3.5; /*使用auto 定义一个变量,在C语言中可以不初始化,编译器会使用一个随机值 值初始化变量val,但建议在写代码时进行初始化,为了说明与C++中的不同,这里 不予初始化*/ auto val; val = value1 + value2; printf("%d\n", val); system("pause"); return 0; }register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
#include<stdio.h> void main() { register int i; int tmp = 0; for (i = 1; i <= 100; i++) tmp += i; printf("The sum is %d\n", tmp); }volatile:说明变量在程序执行中可被隐含地改变
volatile可理解为“编译器警告指示字” volatile用于告诉编译器必须每次去内存中取变量值 volatile主要修饰被多个线程访问的变量 volatile也可以修饰可能被未知因数更改的变量
volatile:与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值
#include <stdio.h> #include <stdlib.h> /* run this program using the console pauser or add your own getch, system("pause") or input loop */ const int* func() { static int count=0; count++; return &count; } int main(int argc, char *argv[]) { volatile int i=0; volatile const int* p=func(); printf("%d\n",*p); return 0; }extern:声明变量是在其他文件正声明
// test.c #include <stdio.h> /* extern "C" { int add(int a, int b) { return a + b; } } */ extern int g; extern int get_min(int a, int b); int main() { return 0; } // test2.c int g = 100; int get_min(int a, int b) { return (a < b) ? a : b; }希望关注点赞评论,传递知识 想要关注我一起学习的请关注公众号:莫林的日常 想要和志同道合的伙伴一起学习的请加QQ群:515271405