首页 > 代码库 > C语言学习笔记
C语言学习笔记
————————————————————————————————————————————
getchar /putchar 输入输出
语法:
c = getchar(); // 获取控制台输入
putchar(c); //输出
————————————————————————————————————————————
EOF,End Of File,文件尾标志。 从数值上来看,就是整数-1
linux:在输入回车换行后的空行位置,按 ctrl+d (先按ctrl键,不放,再按d键)
windows:在输入回车换行后的空行位置,按 ctrl+z,再回车确认
————————————————————————————————————————————
参数传值调用
在C语言中,被调函数不能直接修改主调函数中变量的值,而只能修改函数私有的临时副本的值
必要时,也能够修改主调函数中的变量。需要向被调用函数提供待设置值的变量的地址(指针)。被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。
如果是数组参数,当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址,并不复制数组元素的本身。在被调函数中,可以通过数组下标访问或修改数组元素的值。
————————————————————————————————————————————
变量
- 自动变量(局部变量) //在一个函数开头或段开头处说明的变量
作用域:
仅在定义它的函数内;
初始化:
不自动赋初值,使用前需要赋值;
值的保持:
随函数的引用而存在和消失,不保持值;
- 外部变量(全程变量) //在函数外部定义的变量
优势:
解决函数单独编译的协调;与变量初始化有关;外部变量的值是永久的;解决数据共享;
作用域:
整个程序;
初始化:
0
值的保持:
永久保持
特点:
- c程序可以分别放在几个文件上,每个文件可以作为一个编译单位分别进行编译。外部变量只需在某个文件上定义一次,其它文件若要引用此变量时,应用extern加以说明(外部变量定义时不必加extern关键字)
- 在同一文件中,若前面的函数要引用后面定义的外部(在函数之外)变量时,在函数里加extern加以说明。
举例:
- 静态变量 - 内部 //在局部变量前加上static
作用域:
仅在定义它的函数内;
初始化:
0;
值的保持:
当函数执行完,返回调用点时,该变量并不撤销,再次调用时,其值将继续存在;
特点:
采用静态存贮分配(由编译程序在编译时分配,而一般的自动变量和函数形参均采用动态存贮分配,即在运行时分配空间);
- 静态变量 - 外部 //在函数外部定义的变量前加static
优势:
可以实现数据隐藏
作用域:
定义它的文件(该文件的私有变量),其他文件上的函数不允许访问;
初始化:
0;
- 寄存器变量 //只有自动(局部)变量和函数参数才能进一步指定为寄存器存贮类
特点:
- 使用register变量可以提高存取速度,但寄存器变量的数目依赖于具体机器,声明多了也只有前几个有效。
- 只限于int,char,short ,unsigned和指针类型用寄存类。
- 不能对register变量取地址(即&操作)
————————————————————————————————————————————
常量
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum 枚举常量
不同枚举中的名字必须互不相同,同一枚举中不同的名字可以是用相同的值;相对#define来说,优势在于常量值可以自动生成
举例:
- enum week {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
week是新的数据类型,源于int,此时:
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
THURSDAY = 3
FRIDAY = 4
SATURDAY = 5
SUNDAY = 6
*/
- enum week {MONDAY = 1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
/*
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
*/
- enum escapes {BELL = ‘\a‘, BACKSPACE = ‘\b‘, TAB = ‘\t‘, NEWLINE = ‘n‘, VTAB = ‘\v‘, RETURN = ‘\r‘};
/*
BELL = ‘\a‘,
BACKSPACE = ‘\b‘,
TAB = ‘\t‘,
NEWLINE = ‘n‘,
VTAB = ‘\v‘,
RETURN = ‘\r‘
*/
————————————————————————————————————————————
关键字
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const //限定符,通过 const 对变量进行限定后,无法修改其值,数组同理。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
register //请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率。
- register变量必须是能被CPU所接受的类型。这通常意味着register变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。
- 不能通过 & 来取地址
————————————————————————————————————————————
数组
// 调用数组a[i]的值时也可以写成 *(a+i) 的形式,编译的过程实际上先将其转换成 *(a+i) 再求值。所以 a[i] 与 *(a+i) 是等价的,一个通过数组和下表实现的表达式可以通过指针可偏移量实现。注意:指针是一个变量, p=a或p++是合法的,但数组名不是变量,不可以执行上面的操作。
————————————————————————————————————————————
指针
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
地址运算符 & 只能应用于内存中的对象(变量与数组元素)。它不能作用于表达式/常量/register类型的变量。
指针只能指向某种特定类型的对象(例外情况:指向void类型的指针可以存放指向任何类型的指针,但它不能间接引用其自身)。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针传递
语法:
swap(a,b);
void swap(int *p,int *q)
特点:
- 调用方法传参,仅改变指针p的地址 &p ;
- *p=12赋值,仅改变内存块的值 *p ;
- p=&a指向变量a,仅改变内存块位置 p ;
- 调用方法结束,p的地址和内存块都不变,但如果在调用过程中内存块被修改,则值改变 ;
举例:
#include <stdio.h>
void pointer(int *p)
{
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 2 指针p传入方法pointer中:在新的方法中生成了一个p的拷贝p1,新的地址 6356752,但值和指向的内存块数据没变
*p = 12;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 3 p1改变了其所指向的内存块的值为12,内存块 00F0FF2C 的值变成了22
int a = 11;
p = &a;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 4 p1的值指向a,即p1指向a 内存块 0060FEFC,此时p1与p分别指向不同的内存块了,不会互相影响
}
int main()
{
int b = 22;
int *p = &b;
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 1 *p指向b的地址:获得了p(内存块数据)&p(地址)*p(值)
pointer(p);
printf("\nthe p is %p , addr is %d, *p is %d", p , &p, *p);
//Line 5 方法结束,调用方法结束后,p地址仍是调用前的地址,地址和值没变(改变的仅仅是p的拷贝p1),但是p所指向的内存块数据被p1所改变了,故*p为12
}
输出:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
其他传递方式(来源参考他人)
- 传值方式 //和函数的值传递不是一个概念
语法:
swap(&a , &b);
void swap(int *a , int *b)
// 传入的是变量a的地址 &a,函数接收到的是传入地址的值
// 使用指针方式修改指向内存块的值,传入的是 a、b变量地址的拷贝。
注意:函数的值传递的方式是
swap(a , b);
void swap(int a , int b)
- 传引用方式
语法:
swap(a,b);
void swap(int &p,int &q)
// 使用引用方式,传入的是变量a、b,而不是拷贝,地址不变。
举例:
#include <stdio.h>
void swap(int *a , int *b)
//方法一:传值方式
{
printf("\n\n /*** Method 1 ***/");
printf("\n &a addr : %d , &b addr: %d", &a, &b);
printf("\n a memory : %d , b memory: %d", a, b);
printf("\n *a : %d , *b : %d", *a, *b);
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int &a , int &b)
//方法二:传引用方式
{
printf("\n\n /*** Method 2 ***/");
printf("\n &a addr : %d , &b addr: %d", &a , &b);
int temp = a;
a = b;
b = temp;
}
int main(int argc, char const *argv[])
{
int a = 3 , b = 5;
printf(" &a addr : %d , &b addr: %d", &a , &b);
printf("\n a : %d , b : %d", a , b);
swap(&a , &b); //方法一
printf("\n &a addr : %d , &b addr: %d", &a, &b);
printf("\n a : %d , b : %d", a , b);
swap(a , b); //方法二
printf("\n a : %d , b : %d\n", a , b);
return 0;
}
输出:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*ip自增
语法:
- *ip += 1;
- ++*ip;
- (*ip)++;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针间赋值
语法: iq = ip;
/* 如果iq和ip同样是指向整型的指针,则将ip中的值copy到iq中,iq也将指向ip所指向的对象 */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
参数指针
语法:double atof(char *);
/* atof的参数时一个指向char类型的指针*/
————————————————————————————————————————————
数组指针和指针数组
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
数组指针 //指向数组的指针
定义:int (*a)[4] //表示指向数组a的指针
举例:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[10];
int *p, i;
for (i = 0; i < 10; ++i)
{
scanf("%d", &a[i]);
}
for (p = &a[0]; p < (a + 10); p++)
//p+i = a+i = &a[i] 这三者意思相同,所以循环结构使用 p < (a + 10),等价于 i < 10
{
printf("%d ", *p);
}
return 0;
}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
指针数组 //用于存储指针的数组
定义:int* a[4]; //表示a中的元素都为int型指针
————————————————————————————————————————————
预处理器
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
文件包含 #include "文件名" 或 #include <文件名>
- 用" "引起来,则在源文件所在的位置查找该文件;
- 如果在该位置没有找到文件或用< >括起来的,则根据相应的规则查找该文件,这个规则同具体的实现有关
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
两个预处理语句 #ifdef 和 #ifndef
用法:
#ifndef HDR
#define HDR
/* hdr.h文件的内容在这里*/
#endif
/* 用来测试某个名字是否已经定义,等价于 */
#if !defined(HDR)
#define HDR
#endif
举例:
/* 测试SYSTEM变量后确定包含某个文件 */
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif
#include HDR
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
宏替换
- #define 名字 替换文本
用法:较长的替换文本可以在待续的行末尾加上反斜杠符
举例:
- 例:#define forever for(;;)
/* 该句为无限循环定义了一个新名字forever */
- 例:#define max(A,B) ((A)>(B)?(A):(B))
/* 形参A和B每次出现都将被替换成对应的实际参数,不过要适当使用括号来规范计算次序的正确性 */
- 例:#define dprint(expr) printf(#expr " = %g\n",expr)
/* 当使用时dprintf(x/y);时被扩展为 printf("x/y" "= %g\n",x/y); */
- 例:#define paste(front,back) front ## back
/*预处理器运算符## 为宏定义提供了连接实际参数的手段*/
/* 在调用paste(name,1)时,将建立记好 name1*/
- #undef getchar
int getchar(void){...}
用法:通过#undef 取消名字的宏定义,这样可以保证后续的调用是函数调用,而不是宏调用
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
C语言学习笔记