文件视图
二进制视图
文本文件
二进制文件
通常,对于文本文件使用文本视图,对于二进制文件使用二进制视图。
但是,您也可以使用任一视图处理任一文件。
MS-DOS 文本文件用回车符和换行符的组合 \r\n 来表示结尾。
Macintosh 文本文件只使用一个回车符 \r 来表示结尾。
C 程序使用一个 \n 表示结尾。
如果C程序以文本视图模式处理一个MS-DOS文本文件,在读取文件时它会 将 \r\n 转换为 \n ,在写入文件时将 \n 转换为 \r\n 。
而使用二进制视图,程序将会看到文件中的 \r 和 \n 字符,没有任何映射发生。
UNIX仅采用一种文件结构,所以这两种视图在UNIX实现中就是相同的。
低级I/O:使用操作系统提供的基本I/O服务。
标准高级I/O:使用标准的C库函数包和stdio.h头文件的定义。
C 程序自动帮我们打开3个文件:标准输入,标准输出和标准错误输出。
标准I/O包相对于低级I/O有两点优势:
标准I/O包中有很多专用的函数。
对输入和输出进行了缓冲。
“r” 打开一个文本文件,可以读取文件
“w” 打开一个文本文件,可以写入文件,先将文件的长度截为零。如果该文件不存在则先创建之。
“a” 打开一个文本文件,可以写入文件,向已有文件的尾部追加内容。如果该文件不存在则先创建之。
“r+” 打开一个文本文件,可以进行更新,也可以读取和写入文件。如果该文件不存在,则无法打开。
“w+” 打开一个文本文件,可以进行更新(读取和写入),如果该文件存在则先将其长度截为零,如果不存在则先创建之。
“a+” 打开一个文本文件,可以进行更新,向已有的文件尾部追加内容。如果该文件不存在则先创建之。 可以读取整个文件,但写入时只能追加内容。
“rb” “wb” “ab” “ab+” “a+b” “wb+” “w+b” “rb+” “r+b”
与前面的模式相似,只是使用二进制模式而非文本模式打开文件。
对于像 Unix 和 Linux 这样只有一种文件类型的系统,带 b 字母的模式和不带 b 字母的模式是相同的。
Tips:使用任何一种 w 模式打开一个已有的文件,文件内容将被删除,以便程序以一个空文件开始操作。
如果不能打开文件,fopen() 返回空指针。
ch = getchar(); //从标准输入获得一个字符。
ch = getc(fp); //从指针fp指定的文件中获得一个字符。
putc(ch, fpout);
putc(ch, stdout);
putchar();
C 输入函数直到尝试读取超出文件结尾的时候才会检测到文件结尾。
这意味着应该在一次尝试读取之后立即进行文件结尾判断。
int ch;
FILE * fp = fopen("wacky.txt", "r");
while ((ch = getc(fp) != EOF)
{
putchar(ch);
}
SEEK_SET 文件开始
SEEK_cur 文件当前位置
SEEK_END 文件结尾
fseek(fp, 0, SEEK_SET); 文件开始处
fseek(fp, 10, SEEK_SET); 找到文件的第10个字符
fseek(fp, 2, SEEK_cur); 当前位置向前移动2个字节
fseek(fp, 0, SEEK_END); 文件结尾
fseek(fp, -10, SEEK_END); 从文件结尾处退回10个字节
代码块作用域:在代码中定义的变量具有代码块作用域。
文件作用域:一个在所有函数之外定义的变量具有文件作用域。
函数原型作用域:
函数作用域:
外部链接(external linkage):
内部链接(internal linkage):
空链接(no linkage): 具有代码块作用域或者函数原型作用域的变量有空链接。
int giants = 5; //文件作用域,外部链接
static int dodgers = 3; //文件作用域,内部链接
int main()
{
}
静态存储时期:如果一个变量具有静态存储时期,它在程序执行期间将一直存在。
自动存储时期:
存储类 | 时期 | 作用域 | 链接 | 声明方式 |
自动 | 自动 | 代码块 | 空 | 代码块内 |
寄存器 | 自动 | 代码块 | 空 | 代码块内,只用关键字register |
具有外部链接的静态 | 静态 | 文件 | 外部 | 所有函数之外 |
具有内部链接的静态 | 静态 | 文件 | 内部 | 所有函数之外,使用关键字static |
空连接的静态 | 静态 | 代码块 | 空 | 代码块内,使用关键字static |
自动变量不会自动初始化,
int main()
{
int repid;
int tents = 5;
}
tents 初始化为5, 而repid是任意值。
寄存器变量多是存放在一个寄存器而非内存中,所以无法获得寄存器变量的地址。
register 声明一个寄存器变量仅是一个请求,而非一条直接命令。 所以,变量有可能只是一个普通的自动变量,然而,您依然不能对它使用 地址运算符。
外部变量会自动初始化为零。
只可以使用常量表达式来初始化文件作用域变量。
int tern = 1; //定义声明
extern int tern; //引用声明
const float * pf; //指向一个常量浮点数值
float const * pf; //指向一个常量浮点数值
float * const pf; //pf必须总是指向同一个地址。
/* file1.c */
const double PI = 3.141592;
/* file2.c */
extern const double PI;
/* constant.h */
static const double PI = 3.141592; //不加static会出现重定义
/* file1.c */
#include "constant.h"
/* file2.c */
#include "constant.h"
/* constant.c */
#include "constant.h"
const double PI = 3.141592;
/* constant.h */
extern const double PI;
/* file1.c */
#include "constant.h"
/* file2.c */
#include "constant.h"
字符串是以空字符 \0 结尾的char数组。
如果字符串文字中间没有间隔或者间隔是空格,C语言会将其串联起来。下面两个是等价的。
char greeting[50] = "Hello, and " "how are " "you " "today!";
char greeting[50] = "Hello, and how are you today!";
字符串常量属于静态存储。静态存储是指如果在一个函数中使用字符串常量, 即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。 整个引号中的内容作为指向该字符串存储位置的指针。
const char m1[40] = “Limit yourself”;
字符数组名也是数组首元素的地址。
m1 == &m1[0]
*m1 == ‘L’
*(m1+1) == m1[1] == ‘i’
char heart[] = “I Love you”;
char * head = “I Love you”;
heart是个常量,head是个变量。
都是在静态存储区为字符串分配空间,heart表示的就是第一个字节的地址, 而系统还需要为head分配一个指针的存储位置。
数组的初始化是从静态存储区把一个字符串复制给数组, 而指针的初始化只是复制字符串的地址。
const char * mytal[5] = {“Adding numbers”, “huang jian”};
常量数组
const int days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
数组的初始化
- 如果不初始化数组,数组的每个元素都是无用的数值。
- 部分初始化数组,那么未初始化的元素被设置为零。
int arr[10]; 未初始化
int arr[10] = {100, 200}; a[0]=100, a[1]=200, 其它的都是零
int arr[10] = {0}; 整个数组都是零
sizeof 计算数组中元素的数目。
const int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int nums = sizeof days / sizeof days[0];
sizeof 给出其后的对象或类型的大小(以字节为单位)。
float rain[5][12];
rain具有5个元素,并且每个元素都是包含12个float数值的数组。
rain[0] 是包含12个float元素的数组。
rain[0][0] 是一个float数。
数组名==数组首元素的地址
int zippo[5][2];
int (*pz)[2];
pz = zippo;
zippo == &zippo[0]
zippo[0] == &zippo[0][0]
zippo[m[n] = *(*(zippo+m)+n)
pz[m[n] = *(*(pz+m)+n)
zippo == &zippo[0]
zippo+2 == &zippo[2]
*(zippo+2) == zippo[2]
arr[n]的意思是 *(arr+n),即寻址到内存中的arr,然后移动n个单位,再取出值。
指针Tips:
指针的数值就是它所指的对象的地址。对于包含n个字节的数据类型,对象的地址通常指的是其首字节的地址。
在指针前用运算符星号就可以得到该指针所指向的对象的值。
对指针加1,等价于对指针的值加上它指向的对象的字节大小。
int sum(int * ar, int n);
int sum(int ar[], int n);
在这里,int ar[] 也是表示ar是指向int的指针。
int sum(const int ar[], int n);
加上const,在该函数中则不能修改指针ar指向的内容。
void fun(int (*pt)[4]);
void fun(int pt[][4]);
typedef 数组
typedef int arr4[4]; //arr4是4个int的数组。
typedef arr4 arr3x4[3]; //arr3x4是3个arr4数组
int sum2(arr3x4 ar, int rows);
int sum2(int ar[3][4], int rows); //这里的3会被自动忽略,不起作用
int sum2(int ar[][4], int rows);
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "list.h"
#include "supporting_functions.h"
void vTask1(void * pvParameters);
void vTask2(void * pvParameters);
TaskHandle_t xTask2Handle;
int main()
{
xTaskCreate(vTask1, "Task 1", 1000, NULL, 2, NULL);
xTaskCreate(vTask2, "Task 2", 1000, NULL, 1, &xTask2Handle);
vTaskStartScheduler();
for (;;);
return 0;
}
void vTask1(void * pvParameters)
{
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
for (;;)
{
vPrintString("Task1 is running\n");
vPrintString("About to raise the Task2 priority\n");
vTaskPrioritySet(xTask2Handle, (uxPriority + 1));
}
}
void vTask2(void * pvParameters)
{
UBaseType_t uxPriority = uxTaskPriorityGet(NULL);
for (;;)
{
vPrintString("Task2 is running\n");
vPrintString("About to lower the Task2 priority\n");
vTaskPrioritySet(NULL, (uxPriority - 2));
}
}
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "list.h"
#include "supporting_functions.h"
#define mainDELAY_LOOP_COUNT (0xffffff)
void vContinuousProcessingTask(void * pvParameters);
void vPeriodicTask(void * pvParameters);
const char * pcTextForTask1 = "Continuous task 1 is running\n";
const char * pcTextForTask2 = "Continuous task 2 is running\n";
const char * pcTextForPeriodicTask = "Periodic task is running\n";
int main(void)
{
xTaskCreate(vContinuousProcessingTask, "Task 1", 1000, (void*)pcTextForTask1, 1, NULL);
xTaskCreate(vContinuousProcessingTask, "Task 2", 1000, (void*)pcTextForTask2, 1, NULL);
xTaskCreate(vPeriodicTask, "Task 3", 1000, (void*)pcTextForPeriodicTask, 2, NULL);
vTaskStartScheduler();
for (;;);
return 0;
}
void vContinuousProcessingTask(void * pvParameters)
{
char * pcTaskName = (char*)pvParameters;
for (;;)
{
vPrintString(pcTaskName);
}
}
void vPeriodicTask(void * pvParameters)
{
const TickType_t xDelay250ms = pdMS_TO_TICKS(250UL);
TickType_t xLastWakeTime = xTaskGetTickCount();
for (;;)
{
vPrintString("Periodic task is running\n");
vTaskDelayUntil(&xLastWakeTime, xDelay250ms);
}
}