printf源代码实现

合集下载

百条 C语言 经典源码程序

百条 C语言 经典源码程序

【百条C语言经典源码程序】~~【程序1】题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?1.程序分析:可填在百位、十位、个位的数字都是1、2、3、4。

组成所有的排列后再去掉不满足条件的排列。

2.程序源代码:main(){int i,j,k;printf("\n");for(i=1;i<5;i++)/*以下为三重循环*/for(j=1;j<5;j++)for (k=1;k<5;k++){if (i!=k&&i!=j&&j!=k) /*确保i、j、k三位互不相同*/printf("%d,%d,%d\n",i,j,k);}}【程序3】题目:一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?1.程序分析:在10万以内判断,先将该数加上100后再开方,再将该数加上268后再开方,如果开方后的结果满足如下条件,即是结果。

请看具体分析:2.程序源代码:#include "math.h"main(){long int i,x,y,z;for (i=1;i<100000;i++){ x=sqrt(i+100); /*x为加上100后开方后的结果*/y=sqrt(i+268); /*y为再加上168后开方后的结果*/if(x*x==i+100&&y*y==i+268)/*如果一个数的平方根的平方等于该数,这说明此数是完全平方数*/printf("\n%ld\n",i);}}==============================================================【程序4】题目:输入某年某月某日,判断这一天是这一年的第几天?1.程序分析:以3月5日为例,应该先把前两个月的加起来,然后再加上5天即本年的第几天,特殊情况,闰年且输入月份大于3时需考虑多加一天。

vsprintf源代码

vsprintf源代码
return simple_strtoull(cp,endp,base);
}
static int skip_atoi(const char **s)
{
int i=0;
while (isdigit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1 /* pad with zero */
result = result*base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
//EXPORT_SYMBOL(simple_strtoul);
/**
* simple_strtol - convert a string to a signed long
if (type & SPECIAL) {
if (base==8) {
if (buf <= end)
*buf = '0';
++buf;
} else if (base==16) {
if (buf <= end)
*buf = '0';
++buf;
if (buf <= end)
/**
* simple_strtoll - convert a string to a signed long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here

printf.c源代码

printf.c源代码

printf.c源代码1.引言在计算机编程中,pr i nt f是一个常用的输出函数。

它可以将指定的内容以可读的格式打印到屏幕上。

本文将介绍一个简单的p ri nt f示例程序。

2.示例代码下面是一个使用p rin t f函数的示例代码:#i nc lu de<s td io.h>i n tm ai n(){i n tn um=10;f l oa tp i=3.1415;c h ar ch='A';p r in tf("整数:%d\n",nu m);p r in tf("浮点数:%f\n",p i);p r in tf("字符:%c\n",ch);r e tu rn0;}3.实例解析在上述示例代码中,我们使用了`st di o.h`头文件,这是C语言标准库中的一个常用头文件,它声明了pr in tf函数和其他输入输出相关的函数。

在m ai n函数中,我们定义了三个变量:`n um`为整数类型,`p i`为浮点数类型,`c h`为字符类型。

接下来,我们使用pr i nt f函数按指定的格式输出这些变量的值。

`%d`用于输出整数,`%f`用于输出浮点数,`%c`用于输出字符。

注意,在`pr in tf`函数中,格式占位符(%d、%f、%c等)需要与被输出的变量类型相匹配。

最后,我们返回0来表示程序执行成功。

4.程序输出当我们运行上述示例代码时,将得到如下输出:整数:10浮点数:3.141500字符:A这表明p ri nt f函数成功地将变量的值输出到了屏幕上,并根据指定的格式进行了格式化。

5.总结本文介绍了一个简单的p ri nt f示例程序,并解析了其代码逻辑和输出结果。

pr in tf函数是C语言中非常常用的函数之一,熟练掌握它的使用对于开发C语言程序非常重要。

编程源代码

编程源代码

《C语言程序设计教程》例题源码第一章【例1.1】打印“九江职业大学”的汉语拼音。

程序源码如下:main(){printf(“jiu jiang zhi ye da xue!”);}【例1.2】求任意两个整数的积。

程序源码如下:#include <stdio.h>int mul(int a,int b);main(){int x,y,z;printf("input two numbers:\n");scanf("%d%d",&x,&y);z=mul(x,y);printf("mul=%d",z);}int mul(int a,int b){int c;c=a*b;return c;}【例1.3】求整数a、b的和。

程序源码如下:main(){int a, b;int sum;scanf(“%d,%d”,&a,&b);sum = a + b;printf(“%d”,sum);}【例2.1】转义字符的使用。

程序源码如下:main(){printf("\x4f\x4b\x21\n");printf("\112 \112\132\104\n");}【例2.2】符号常量的使用。

程序源码如下:#define PI 3.14159265main(){float r,m,s;r=1.2;m=2* PI *r;s= PI *r*r;printf("%f,%f",m,s);}【例2.3】整型变量的定义与使用。

程序源码如下:main(){int x, y, m;unsigned u, n;x=30; y=18; u=32800;m=x+y; n=y+u;printf("x+y=%d, y+u=%d\n",m,n); }【例2.4】整型变量的定义与使用。

C语言简单计算器程序源代码

C语言简单计算器程序源代码

C语言简单计算器程序源代码以下是一个简单计算器程序的C语言源代码,超过1200字: ```c#include <stdio.h>int maichar operator;double num1, num2, result;printf("请输入操作数1: ");scanf("%lf", &num1);printf("请输入操作符(+, -, *, /): ");scanf(" %c", &operator);printf("请输入操作数2: ");scanf("%lf", &num2);switch (operator)case '+':result = num1 + num2;printf("%.2lf + %.2lf = %.2lf\n", num1, num2, result); break;case '-':result = num1 - num2;printf("%.2lf - %.2lf = %.2lf\n", num1, num2, result); break;case '*':result = num1 * num2;printf("%.2lf * %.2lf = %.2lf\n", num1, num2, result); break;case '/':if (num2 != 0)result = num1 / num2;printf("%.2lf / %.2lf = %.2lf\n", num1, num2, result); } elseprintf("错误:被除数不能为0!\n");}break;default:printf("错误:无效的操作符!\n");break;}return 0;```这个程序实现了一个简单的计算器,可以根据用户输入的操作数和操作符进行加、减、乘、除的四则运算,并输出结果。

经典C语言源代码

经典C语言源代码

经典C语言源代码1、(1)某年某月某日是星期几#include<stdio.h>int main(){int year, month, day;while (scanf_s("%d%d%d", &year, &month, &day) != EOF){if (month == 1 || month == 2)//判断month是否为1或2{year--;month += 12;}int c = year / 100;int y = year - c * 100;int week = (c / 4) - 2 * c + (y + y / 4) + (13 * (month + 1) /5) + day - 1;while (week<0) { week += 7; }week %= 7;switch (week){case 1:printf("Monday\n"); break;case 2:printf("Tuesday\n"); break;case 3:printf("Wednesday\n"); break;case 4:printf("Thursday\n"); break;case 5:printf("Friday\n"); break;case 6:printf("Saturday\n"); break;case 0:printf("Sunday\n"); break;}}return 0;}1、(2)某年某月某日是第几天(一维数组)#include "stdio.h"void main() {int i, flag, year, month, day, dayth;int month_day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };printf("请输入年/月/日:\n");scanf_s("%d/%d/%d", &year, &month, &day);dayth = day;flag = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);if (flag)month_day[2] = 29;for (i = 1; i < month; i++)dayth = dayth + month_day[i];printf("%d/%d/%d是第%d天\n", year, month, day, dayth); }2、30个数中找最小的数及其位置#include "stdio.h"# define SIZE 30void main() {int i;float data[SIZE];int min;printf("请输入%d个浮点数:\n",SIZE);for (i = 0; i < SIZE; i++) {//scanf_s("%f", &data[i]);data[i] = rand() % 30 + 1;printf("%f、", data[i]);}min = 0;for (i = 1; i < SIZE; i++) {if (data[i] < data[min])min = i;}printf("最小值是%5.2f,位置是%5d\n", data[min], min); }3、30个数从小到大排序(1)#include "stdio.h"# define SIZE 30void main() {int i,j;float data[SIZE],temp;int min;printf("请输入%d个整型数:\n",SIZE);for (i = 0; i < SIZE; i++) {scanf_s("%f", &data[i]);}for (i = 0; i < SIZE; i++) {min = i;for (j = i + 1; j < SIZE; j++)if (data[j] < data[min])min = j;temp = data[min];data[min] = data[i];data[i] = temp;}printf("\n排序后的结果是:\n");for (i = 0; i < SIZE; i++)printf("%5.2f", data[i]);}(2)模块化程序(数组名作为函数参数)#include "stdio.h"# define SIZE 5void accept_array(float a[], int size); void sort(float a[], int size);void show_array(float a[], int size);void main() {float score[SIZE];accept_array(score, SIZE);printf("排序前:");show_array(score, SIZE);sort(score, SIZE);printf("排序后:");show_array(score, SIZE);}void accept_array(float a[], int size) {int i;printf("请输入%d个分数:", size);for (i = 0; i < size; i++)scanf_s("%f", &a[i]);}void show_array(float a[], int size) { int i;for (i = 0; i < size; i++)printf(" %5.2f", a[i]);printf("\n");}void sort(float a[],int size) {int i, min, j;float temp;for (i = 0; i < SIZE; i++) {min = i;for (j = i + 1; j < SIZE; j++)if (a[j] < a[min])min = j;temp = a[min];a[min] = a[i];a[i] = temp;}}4、(1)指针加减:#include "stdio.h"#define SIZE 10void main() {int a[SIZE] = { 1,2,3,4,5,6,7,8,9,10 };int *pa, i;pa = &a[0];//pa=a;printf("\n");for (i = 0; i < SIZE; i++) {printf(" %d", *pa);//printf(" %d", *(pa+1));pa++;}}(2)指针比较:#include "stdio.h"#define SIZE 10void main() {int a[SIZE] = { 1,2,3,4,5,6,7,8,9,10 };int *pa, i;int *qa;pa = qa = &a[0];printf("请输入%d整型数:",SIZE);for (; pa < qa + SIZE; pa++)scanf_s("%d", pa);for (pa--; qa <= pa; pa--)printf(" %d", *pa);}5、两字符串相连:#include "stdio.h"#include "string.h"void str_cat(char str1[], char str2[]); void main() {int i, j;char str1[160];char str2[80];printf("请输入第一个字符串:");gets(str1);printf("请输入第二个字符串:");gets(str2);str_cat(str1, str2);puts(str1);}void str_cat(char str1[], char str2[]) { int i, j;i = 0;while (str1[i] != '\0')i++;j = 0;while (str2[j] != '\0') {str1[i] = str2[j];i++; j++;}str1[i] = '\0';}6、二维数组(a,b转置)#include "stdio.h"void main() {int i, j, b[2][3];int a[3][2] = { {1,2},{3,4},{5,6} }; for (i = 0; i < 2; i++) {for (j = 0; j < 3; j++)b[i][j] = a[j][i];}printf("\na:\n");for (i = 0; i < 3; i++) {for (j = 0; j < 2; j++)printf("%5d", a[i][j]);printf("\n");}printf("\nb:\n");for(i = 0; i < 2; i++) {for (j = 0; j < 3; j++)printf("%5d", b[i][j]);printf("\n");}7、输入一个二维数组并输出(指针)#include "stdio.h"void main() {int x[2][3];int i, j;for (i = 0; i < 2; i++)for (j = 0; j < 3; j++)scanf_s("%d", *(x + i) + j);putchar('\n');for (i = 0; i < 2; i++){for (j = 0; j < 3; j++)printf("%d ", *(*(x + i) + j));putchar('\n');}8、冒泡法排序一个数组#include "stdio.h"#define size 10void maopao(int a[]);void main() {int a[10];int i;printf("请输入10个整数:\n");for (i = 0; i < 10; i++)scanf_s("%d", &a[i]);maopao(a);}void maopao(int a[]) {int i, j, temp;for (i = 0; i < 9; i++) {//进行9轮排序for (j = 0; j < 9 - i; j++)//每轮进行9-i次交换{if (a[j] > a[j + 1]){temp = a[j];a[j] = a[j + 1];//大的沉底,小的上浮a[j + 1] = temp;}}}printf("排序结果:\n");for (i = 0; i < 10; i++)printf("%4d", a[i]);}9、两数组A,B,要求A<B,如A:4,7,9B:1,3,5,8,9变换后A:1,3,5B:4,7,8,9,9#include <stdio.h>void ReArranger(int* A, int* B, int m, int n) //A和B是各有m个和n个整数的非降序数组,本算法将B数组元素逐个插入到A 中,使A中各元素均不大于B中各元素,且两数组仍保持非降序排列。

printf函数实现

printf函数实现

int printf(const char *fmt, ...){int i;char buf[256];va_list arg = (va_list)((char*)(&fmt) + 4);i = vsprintf(buf, fmt, arg);write(buf, i);return i;}代码位置:D:/~/funny/kernel/printf.c在形参列表里有这么一个token:...这个是可变形参的一种写法。

当传递参数的个数不确定时,就可以用这种方式来表示。

很显然,我们需要一种方法,来让函数体可以知道具体调用时参数的个数。

先来看printf函数的内容:这句:va_list arg = (va_list)((char*)(&fmt) + 4);va_list的定义:typedef char *va_list这说明它是一个字符指针。

其中的:(char*)(&fmt) + 4) 表示的是...中的第一个参数。

如果不懂,我再慢慢的解释:C语言中,参数压栈的方向是从右往左。

也就是说,当调用printf函数的适合,先是最右边的参数入栈。

fmt是一个指针,这个指针指向第一个const参数(const char *fmt)中的第一个元素。

fmt也是个变量,它的位置,是在栈上分配的,它也有地址。

对于一个char *类型的变量,它入栈的是指针,而不是这个char *型变量。

换句话说:你sizeof(p) (p是一个指针,假设p=&i,i为任何类型的变量都可以)得到的都是一个固定的值。

(我的计算机中都是得到的4)当然,我还要补充的一点是:栈是从高地址向低地址方向增长的。

ok!现在我想你该明白了:为什么说(char*)(&fmt) + 4) 表示的是...中的第一个参数的地址。

下面我们来看看下一句:i = vsprintf(buf, fmt, arg);让我们来看看vsprintf(buf, fmt, arg)是什么函数。

(整理)C语言入门经典案例及源代码.

(整理)C语言入门经典案例及源代码.

循环控制输出图案【程序1】题目:输出9*9口诀。

1.程序分析:分行与列考虑,共9行9列,i控制行,j控制列。

2.程序源代码:#include "stdio.h"main(){int i,j,result;printf("\n");for (i=1;i<10;i++){ for(j=1;j<10;j++){result=i*j;printf("%d*%d=%-3d",i,j,result);/*-3d表示左对齐,占3位*/}printf("\n");/*每一行后换行*/}}【程序2】题目:要求输出国际象棋棋盘。

1.程序分析:用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方格,还是白方格。

2.程序源代码:#include "stdio.h"main(){int i,j;for(i=0;i<8;i++){for(j=0;j<8;j++)if((i+j)%2==0)printf("%c%c",219,219);elseprintf(" ");printf("\n");}}==============================================================【程序3】题目:打印楼梯,同时在楼梯上方打印两个笑脸。

1.程序分析:用i控制行,j来控制列,j根据i的变化来控制输出黑方格的个数。

2.程序源代码:#include "stdio.h"main(){int i,j;printf("\1\1\n");/*输出两个笑脸*/for(i=1;i<11;i++){for(j=1;j<=i;j++)printf("%c%c",219,219);printf("\n");}}【程序4】题目:打印出如下图案(菱形)****************************1.程序分析:先把图形分成两部分来看待,前四行一个规律,后三行一个规律,利用双重for循环,第一层控制行,第二层控制列。

c语言中 printf格式

c语言中 printf格式

在C语言中,printf函数用于格式化输出。

它使用格式字符串来指定输出格式,并允许你插入变量或表达式。

以下是一些常用的格式说明符:
1.%d或%i:用于输出十进制整数。

2.%u:用于输出无符号十进制整数。

3.%f:用于输出浮点数。

4.%s:用于输出字符串。

5.%c:用于输出字符。

6.%p:用于输出指针的值。

7.%o:用于输出八进制数。

8.%x或%X:用于输出十六进制数。

9.%e或%E:用于输出科学计数法的浮点数。

10.%g或%G:用于自动选择最佳格式的浮点数。

还有一些格式说明符可以用于更复杂的输出控制,例如宽度、精度、标志等。

以下是一个简单的例子,展示了如何使用printf函数:
c复制代码
#include<stdio.h>
int main() {
int a = 10;
float b = 3.14;
char c = 'A';
printf("整数:%d\n", a);
printf("浮点数:%f\n", b);
printf("字符:%c\n", c);
return0;
}
输出结果为:
makefile复制代码
整数:10
浮点数:3.140000
字符:A。

c语言printf函数源码

c语言printf函数源码

c语言printf函数源码【原创版】目录1.C 语言 printf 函数简介2.printf 函数的参数3.printf 函数的格式控制字符串4.printf 函数的返回值5.printf 函数的源代码分析正文1.C 语言 printf 函数简介printf 函数是 C 语言中用于输出信息的函数,它可以将数据按照指定的格式输出到屏幕上。

printf 函数是库函数,需要在程序中使用之前包含头文件<stdio.h>。

2.printf 函数的参数printf 函数的参数主要包括两部分:格式控制字符串和要输出的参数表。

格式控制字符串用于指定输出信息的格式,而参数表中包含了要输出的实际数据。

3.printf 函数的格式控制字符串格式控制字符串是一个字符串,其中包含了普通字符、转义字符和格式控制符。

普通字符和转义字符将被原样输出,而格式控制符用于指定要输出的数据的类型和格式。

4.printf 函数的返回值printf 函数的返回值是成功输出的字符数量,如果输出失败则返回负数。

5.printf 函数的源代码分析以下是 printf 函数的简要源代码分析(以 GCC 编译器为例):```cint printf(const char *format,...);{va_list args;int count;unsigned int i;char *str;char *ptr;int ret = 0;va_start(args, format);while ((str = va_arg(args, char *))!= NULL){count = 0;for (i = 0; str[i]!= "0"; i++){count++;if (str[i] == ""){count++;break;}}if (count > 0){for (ptr = &str[count - 1]; ptr >= str; --ptr) {putchar(*ptr);}ret = count;}}va_end(args);return ret;}```从源代码可以看出,printf 函数首先使用 va_list 类型变量 args 保存参数表,然后遍历参数表中的每个参数,将其转换为字符串并计算字符串的长度。

C语言源代码

C语言源代码

C语言源代码C 语言作为一门经典的编程语言,在计算机科学领域中具有举足轻重的地位。

C 语言源代码是用 C 语言编写的程序的原始文本形式,它是程序员思想的具体体现,也是计算机能够理解和执行的指令集合。

C 语言源代码的基本组成部分包括预处理指令、变量声明、函数定义、控制结构等。

预处理指令通常以“”开头,比如“include <stdioh>”,它用于在编译前对源代码进行一些预处理操作,如包含所需的头文件。

变量声明用于指定程序中使用的数据类型和名称。

C 语言中有多种数据类型,如整型(int)、浮点型(float、double)、字符型(char)等。

例如,“int age =25;”声明了一个名为 age 的整型变量,并初始化为 25。

函数是 C 语言中的重要概念,它将一段具有特定功能的代码封装起来,方便重复使用和代码的组织。

一个简单的函数可能如下所示:```cint add(int a, int b) {return a + b;}```在上述代码中,“add”是函数名,“int”表示函数返回值的类型,“a”和“b”是函数的参数。

控制结构用于决定程序的执行流程,包括顺序结构、选择结构(如ifelse 语句)和循环结构(如 for 循环、while 循环)。

比如,ifelse 语句用于根据条件执行不同的代码块:```cif (age >= 18) {printf("You are an adult\n");} else {printf("You are a minor\n");}```for 循环用于重复执行一段代码一定的次数:```cfor (int i = 0; i < 5; i++){printf("%d\n", i);}```while 循环则在条件为真时持续执行代码:```cint count = 0;while (count < 10) {printf("%d\n", count);count++;}```C 语言源代码的编写需要遵循严格的语法规则。

c语言printf函数用法

c语言printf函数用法

C语言printf函数用法详解1. 函数定义printf函数是C语言标准库stdio.h中的一个函数,用于将格式化的数据输出到标准输出设备(通常是屏幕)。

其函数原型如下:int printf(const char *format, ...);printf函数接受一个格式化字符串作为第一个参数,后面可以跟上多个可选参数,用于填充格式化字符串中的占位符。

函数的返回值是输出的字符数。

2. 用途printf函数是C语言中最常用的输出函数之一,用于在控制台上输出信息。

它可以输出字符串、数字、字符等各种数据类型,并且可以根据需要进行格式化输出。

printf函数的主要用途包括但不限于:•输出提示信息•打印变量的值•调试程序•格式化输出数据•生成日志文件3. 工作方式printf函数的工作方式如下:1.解析格式化字符串:printf函数首先会解析格式化字符串,查找其中的占位符,并根据占位符的类型和格式进行相应的处理。

2.格式化输出数据:根据解析得到的占位符信息,printf函数会从可变参数列表中取出对应类型的值,并根据占位符的格式进行格式化输出。

3.输出到标准输出:printf函数将格式化后的字符串输出到标准输出设备上(通常是屏幕),供用户查看。

4. 格式化字符串格式化字符串是printf函数中的第一个参数,用于指定输出的格式和占位符。

格式化字符串由普通字符和占位符组成。

4.1 普通字符普通字符是格式化字符串中不包含占位符的部分,会被原样输出。

例如:printf("Hello, World!"); // 输出:Hello, World!4.2 占位符占位符由百分号(%)和格式控制字符组成,用于指定输出数据的类型和格式。

常用的占位符有:•%d:输出十进制整数•%f:输出浮点数•%c:输出字符•%s:输出字符串•%p:输出指针地址•%x:输出十六进制整数占位符可以通过一些修饰符进行格式化,如:•%10d:输出宽度为10的十进制整数•%.2f:输出小数点后保留两位的浮点数示例代码:int num = 100;printf("The number is %d\n", num); // 输出:The number is 100float pi = 3.14159;printf("The value of pi is %.2f\n", pi); // 输出:The value of pi is 3.14char ch = 'A';printf("The character is %c\n", ch); // 输出:The character is Achar str[] = "Hello";printf("The string is %s\n", str); // 输出:The string is Helloint *ptr = NULL;printf("The address is %p\n", ptr); // 输出:The address is 0x0int hex = 255;printf("The hexadecimal number is %x\n", hex); // 输出:The hexadecimal numbe r is ff5. 可选参数printf函数的可选参数是指格式化字符串中占位符的实际值。

printf 源代码实现

printf 源代码实现

打开Source Insight来阅读EduOS的源代码,我们在stdio.c里找到了printf的实现代码.首先看看对printf的定义:int printf (const char *cntrl_string, ...)第一个参数cntrl_string是控制字符串,也就是平常我们写入%d,%f的地方.紧接着后面是一个变长参数.看看函数头部的定义:int pos = 0, cnt_printed_chars = 0, i;unsigned char* chptr;va_list ap;马上晕!除了ap我们可以马上判断出来是用来读取变长参数的,i用于循环变量.其他变量都不知道是怎么回事.不要着急,我们边看代码边分析.代码的第一行必然是va_start (ap, cntrl_string);用来初始化变长参数.接下来是一个while循环while (cntrl_string[pos]) {...}结束条件是cntrl_string[pos]为NULL,显然这个循环是用来遍历整个控制字符串的.自然pos就是当前遍历到的位置了.进入循环首先闯入视线的是if (cntrl_string[pos] == &#39;%&#39;) {pos++;...}开门见山,上来就当前字符是否办断是否%.一猜就知道如果成立pos++马上取出下一个字符在d,f,l等等之间进行判断.往下一看,果真不出所料:switch (cntrl_string[pos]) {case &#39;c&#39;:...case &#39;s&#39;:...case &#39;i&#39;:...case &#39;d&#39;:...case &#39;u&#39;:...用上switch-case了. 快速浏览一下下面的代码.首先看看case &#39;c&#39;的部分case &#39;c&#39;:putchar (va_arg (ap, unsigned char));cnt_printed_chars++;break;%c表示仅仅输出一个字符.因此先通过va_arg进行参数的类型转换,之后用putchar[1]输出到屏幕上去.之后是cnt_printed_chars++,通过这句我们就可以判断出cnt_printed_chars使用来表示,已经被printf输出的字符个数的.再来看看 case &#39;s&#39;:case &#39;s&#39;:chptr = va_arg (ap, unsigned char*);i = 0;while (chptr [i]) {cnt_printed_chars++;putchar (chptr [i++]);}break;和case &#39;c&#39;,同出一辙.cnt_printed_chars++放在了循环内,也证明了刚才提到的他的作用.另外我们也看到了cnptr是用来在处理字符串时的位置指针.到此为止,我们清楚的所有变量的用途,前途变得更加光明了.接下来:// PartIcase &#39;i&#39;:case &#39;d&#39;:cnt_printed_chars += printInt (va_arg (ap, int));break;case &#39;u&#39;:cnt_printed_chars += printUnsignedInt (va_arg (ap, unsigned int));break;case &#39;x&#39;:cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break;case &#39;X&#39;:cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;X&#39;); break;case &#39;o&#39;:cnt_printed_chars += printOctal (va_arg (ap, unsigned int));break;// Part IIcase &#39;p&#39;:putchar (&#39;0&#39;);putchar (&#39;x&#39;);cnt_printed_chars += 2; /* of &#39;0x&#39; */cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break;case &#39;#&#39;:pos++;switch (cntrl_string[pos]) {case &#39;x&#39;:putchar (&#39;0&#39;);putchar (&#39;x&#39;);cnt_printed_chars += 2; /* of &#39;0x&#39; */cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break;case &#39;X&#39;:putchar (&#39;0&#39;);putchar (&#39;X&#39;);cnt_printed_chars += 2; /* of &#39;0X&#39; */cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;X&#39;); break;case &#39;o&#39;:putchar (&#39;0&#39;);cnt_printed_chars++;cnt_printed_chars += printOctal (va_arg (ap, unsigned int));break;注意观察一下,PartII的代码其实就是比PartI的代码多一个样式.在16进制数或八进制前加入0x或是o,等等.因此这里就只分析一下PartI咯.其实仔细看看PartI的个条case,也就是把参数分发到了更具体的函数用于显示,然后以返回值的形式返回输出个数.对于这些函数就不具体分析了.我们先来看看一些善后处理:先看case的default处理.default:putchar ((unsigned char) cntrl_string[pos]);cnt_printed_chars++;就是直接输出cntrl_string里%号后面的未知字符.应该是一种容错设计处理.再看看if (cntrl_string[pos] == &#39;%&#39;)的else部分else {putchar ((unsigned char) cntrl_string[pos]);cnt_printed_chars++;pos++;}如果不是%开头的,那么直接输出这个字符.最后函数返回前va_end (ap);return cnt_printed_chars;va_end处理变长参数的善后工作.并返回输出的字符个数.在最后我们有必要谈谈putChar函数以及基本输出的基础函数printChar,先来看看putCharint putchar (int c) {switch ((unsigned char) c) {case &#39;\n&#39; :newLine ();break;case &#39;\r&#39; :carriageReturn ();break;case &#39;\f&#39; :clearScreen ();break;case &#39;\t&#39; :printChar (32); printChar (32); /* 32 = space */printChar (32); printChar (32);printChar (32); printChar (32);printChar (32); printChar (32);break;case &#39;\b&#39;:backspace ();break;case &#39;\a&#39;:beep ();break;default :printChar ((unsigned char) c);}return c;}通览一下,也是switch-case为主体的.主要是用来应对一些特殊字符,如\n,\r,....这里需要提一下,关于\t的理解.有些人认为\t就是8个space,有些人则认为,屏幕分为10大列(每个大列8个小列总共80列).一个\t就跳到下一个大列输出.也就是说不管你现在实在屏幕的第1,2,3,4,5,6,7位置输出字符,只要一个\t都在第8个位置开始输出. 中就是用的这种理解.因此如果按照这个理解的话,\t的实现可以这样int currentX = ((currentX % 10) + 1) * 8;然后在currentX位置输出.接下来看printChar也就是输出部分最低层的操作咯void printChar (const byte ch) {*(word *)(VIDEO + y * 160 + x * 2) = ch | (fill_color &lt;&lt; 8);x++;if (x &gt;= WIDTH)newLine ();setVideoCursor (y, x);}这里VIDEO表示显存地址也就是0xB8000.通过 y * 160 + x 屏幕(x,y)坐标在显存中的位置.这里需要知道,一个字符显示需要两个字节,一个是ASCII码,第二个是字符属性代码也就是颜色代码.因此才必须 y * 80 * 2 + x = y * 160 + x.那么ch | (fill_color &lt;&lt; 8)也自然就是写入字符及属性代码用的了.每写一个字符光标位置加1,如果大于屏幕宽度WIDTH就换行.最后通过setVideoCursor设置新的光标位置.完成了整个printChar过程.。

实现自己的printf函数(转载)

实现自己的printf函数(转载)

实现⾃⼰的printf函数(转载)转载⾃:在嵌⼊式开发中,常常会通过串⼝打印⼀些信息到PC终端,这就需要实现⾃⼰的printf函数,下⾯介绍打印函数print的实现。

print.h1 #ifndef __PRINT_H_2#define __PRINT_H_34void print(char* fmt, ...);5void printch(char ch);6void printdec(int dec);7void printflt(double flt);8void printbin(int bin);9void printhex(int hex);10void printstr(char* str);1112#define console_print(ch) putchar(ch)1314#endif /*#ifndef __PRINT_H_*/上⾯print函数为全功能的打印函数,可以实现类似printf的功能,printch实现单个字符的打印、printdec实现⼗进制格式数字的打印,printflt 实现浮点数的打印,printbin实现⼆进制格式数字的打印,printhex实现⼗六进制格式数字的打印,printstr实现字符串的打印,console_print 函数是串⼝单字符打印函数的宏定义,这⾥暂时⽤PC终端单字符打印函数putchar代替。

在实际嵌⼊式环境下,替换成串⼝单字符打印函数即可。

print.c1 #include <stdio.h>2 #include <stdarg.h>3 #include "print.h"45int main(void)6 {7 print("print: %c\n", 'c');8 print("print %d\n", 1234567);9 print("print: %f\n", 1234567.1234567);10 print("print: %s\n", "string test");11 print("print: %b\n", 0x12345ff);12 print("print: %x\n", 0xabcdef);13 print("print: %%\n");14return0;15 }1617void print(char* fmt, ...)18 {19double vargflt = 0;20int vargint = 0;21char* vargpch = NULL;22char vargch = 0;23char* pfmt = NULL;24 va_list vp;2526 va_start(vp, fmt);27 pfmt = fmt;2829while(*pfmt)30 {31if(*pfmt == '%')32 {33switch(*(++pfmt))34 {3536case'c':37 vargch = va_arg(vp, int);38/* va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI39 mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */40 printch(vargch);41break;42case'd':43case'i':44 vargint = va_arg(vp, int);45 printdec(vargint);46break;47case'f':48 vargflt = va_arg(vp, double);49/* va_arg(ap, type), if type is narrow type (char, short, float) an error is given in strict ANSI50 mode, or a warning otherwise.In non-strict ANSI mode, 'type' is allowed to be any expression. */51 printflt(vargflt);52break;53case's':54 vargpch = va_arg(vp, char*);55 printstr(vargpch);56break;57case'b':58case'B':59 vargint = va_arg(vp, int);60 printbin(vargint);61break;62case'x':63case'X':64 vargint = va_arg(vp, int);65 printhex(vargint);66break;67case'%':68 printch('%');69break;70default:71break;72 }73 pfmt++;74 }75else76 {77 printch(*pfmt++);78 }79 }80 va_end(vp);81 }8283void printch(char ch)84 {85 console_print(ch);86 }8788void printdec(int dec)89 {90if(dec==0)91 {92return;93 }94 printdec(dec/10);95 printch( (char)(dec%10 + '0'));96 }9798void printflt(double flt)99 {100int icnt = 0;101int tmpint = 0;102103 tmpint = (int)flt;104 printdec(tmpint);105 printch('.');106 flt = flt - tmpint;107 tmpint = (int)(flt * 1000000);108 printdec(tmpint);109 }110111void printstr(char* str)112 {113while(*str)114 {115 printch(*str++);116 }117 }118119void printbin(int bin)120 {121if(bin == 0)122 {123 printstr("0b");124return;125 }126 printbin(bin/2);127 printch( (char)(bin%2 + '0'));128 }129130void printhex(int hex)131 {132if(hex==0)133 {134 printstr("0x");135return;136 }137 printhex(hex/16);138if(hex < 10)139 {140 printch((char)(hex%16 + '0')); 141 }142else143 {144 printch((char)(hex%16 - 10 + 'a' )); 145 }146 }感觉这个程序写得挺漂亮的。

通过串口实现printf和scanf函数

通过串口实现printf和scanf函数

通过串⼝实现printf和scanf函数在做裸板开发时,常常需要通过输出或者通过串⼝输⼊⼀些信息。

在有操作系统机器上,我们很少关⼼输⼊和输出的问题。

因为有很多现成的库函数供我们调⽤。

在做裸板开发时,可没有现成库函数供我们调⽤,⼀切都需要我们⾃⼰实现。

下⾯我们通过串⼝在裸板上实现⼀个printf和scanf函数。

printf主要⽤来进⾏格式化输出,scanf函数主要⽤来进⾏格式化输⼊的。

这⾥个函数都是不定参数函数,这⾥简单介绍⼀下不定参函数实现⽅法。

⼀、不定参数的造型function(type arg,...);⼀般第⼀个参数arg指定参数的个数,int function(3,arg1,arg2,arg3);这是⼀种显⽰的告诉编译器有⼏个参数,第⼀个参数3,即代表后⾯有三个参数。

但也不是唯⼀的,⽐如printf的第⼀个参数是⼀个字符串,它是通过这个参数来确定有⼏个参数的.int printf(const char *format, ...);例如我们调⽤的时候printf("%d,%s,%d",i,str,j);第⼀个参数是"%d,%s,%d",通过分析它我们可以知道有⼏个⼆、函数的参数压栈1. 固定的参数函数调⽤过程⼀般函数参数⼊栈是按照从右向左的顺序⼊栈。

这样第⼀个参数arg1就放在了栈顶的位置。

2.变参函数调⽤过程通过上⾯的参数⼊栈⽅式我们可以得到如下结论:如果想将栈中的参数读出来,我们只需要知道,栈顶元素的地址即第⼀个参数的地址即可。

通过前⾯变参函数的分析,通过变参函数第⼀个参数可以知道传递的参数个数。

根据参数⼊栈的顺序,我们可以知道第⼀个参数是放在栈顶位置的。

总结⼀下,现在我们已经获得两个条件了:1.栈中参数的个数2.栈顶元素的地址有了这两个条件,我们就可以从栈中读取我们想要的参数了。

当然,每个参数都有⾃⼰的类型,还有的就是字节对齐了。

在读取参数的时候,这些问题都必须考虑到。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
接下来: // PartI case &#39;i&#39;: case &#39;d&#39;: cnt_printed_chars += printInt (va_arg (ap, int)); break; case &#39;u&#39;:
cnt_printed_chars += printUnsignedInt (va_arg (ap, unsigned int)); break; case &#39;x&#39;: cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break; case &#39;X&#39;: cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;X&#39;); break; case &#39;o&#39;: cnt_printed_chars += printOctal (va_arg (ap, unsigned int)); break; // Part II case &#39;p&#39;: putchar (&#39;0&#39;); putchar (&#39;x&#39;); cnt_printed_chars += 2; /* of &#39;0x&#39; */ cnt_printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break; case &#39;#&#39;:
最后函数返回前 va_end (ap); return cnt_printed_chars; va_end 处理变长参数的善后工作.并返回输出的字符个数.
在最后我们有必要谈谈 putChar 函数以及基本输出的基础函数 printChar,先来 看看 putChar
int putchar (int c) { switch ((unsigned char) c) { case &#39;\n&#39; : newLine (); break; case &#39;\r&#39; : carriageReturn (); break; case &#39;\f&#39; : clearScreen ();
pos++; switch (cntrl_string[pos]) { case &#39;x&#39;: putchar (&#39;0&#39;); putchar (&#39;x&#39;); cnt_printed_chars += 2; /* of &#39;0x&#39; */ _printed_chars += printHexa (va_arg (ap, unsigned int), &#39;x&#39;); break; case &#39;X&#39;: putchar (&#39;0&#39;); putcha
cnt_printed_chars++; cnt_printed_chars += printOctal (va_arg (ap, unsigned int)); break;
注意观察一下,PartII 的代码其实就是比 PartI 的代码多一个样式.在 16 进制数或 八进制前加入 0x 或是 o,等等.因此这里就只分析一下 PartI 咯.
用来初始化变长参数.
接下来是一个 while 循环
while (cntrl_string[pos]) { ... }
结束条件是 cntrl_string[pos]为 NULL,显然这个循环是用来遍历整个控制字符 串的.自然 pos 就是当前遍历到的位置了.进入循环首先闯入视线的是
if (cntrl_string[pos] == &#39;%&#39;) { pos++; ... }
看看函数头部的定义:
int pos = 0, cnt_printed_chars = 0, i; unsigned char* chptr; va_list ap;
马上晕!除了 ap 我们可以马上判断出来是用来读取变长参数的,i 用于循环变量. 其他变量都不知道是怎么回事.不要着急,我们边看代码边分析.代码的第一行必 然是 va_start (ap, cntrl_string);
接下来看 printChar 也就是输出部分最低层的操作咯
void printChar (const byte ch) { *(word *)(VIDEO + y * 160 + x * 2) = ch | (fill_color &lt;&lt; 8); x++; if (x &gt;= WIDTH) newLine (); setVideoCursor (y, x); }
这里 VIDEO 表示显存地址也就是 0xB8000.通过 y * 160 + x 屏幕(x,y)坐标在 显存中的位置.这里需要知道,一个字符显示需要两个字节,一个是 ASCII 码,第二 个是字符属性代码也就是颜色代码.因此才必须 y * 80 * 2 + x = y * 160 + x. 那么 ch | (fill_color &lt;&lt; 8)也自然就是写入字符及属性代码用的了.每写一
其实仔细看看 PartI 的个条 case,也就是把参数分发到了更具体的函数用于显示, 然后以返回值的形式返回输出个数.对于这些函数就不具体分析了.我们先来看 看一些善后处理:
先看 case 的 default 处理. default: putchar ((unsigned char) cntrl_string[pos]); cnt_printed_chars++; 就是直接输出 cntrl_string 里%号后面的未知字符.应该是一种容错设计处理.
个字符光标位置加 1,如果大于屏幕宽度 WIDTH 就换
行.最后通过 setVideoCursor 设置新的光标位置.完成 了整个 printChar 过程.
再看看 if (cntrl_string[pos] == &#39;%&#39;)的 else 部分
else { putchar ((unsigned char) cntrl_string[pos]); cnt_printed_chars++;
pos++; }
如果不是%开头的,那么直接输出这个字符.
通 览 一 下 , 也 是 switch-case 为 主 体 的 . 主 要 是 用 来 应 对 一 些 特 殊 字 符 , 如 \n,\r,....这里需要提一下,关于\t 的理解.有些人认为\t 就是 8 个 space,有些人 则认为,屏幕分为 10 大列(每个大列 8 个小列总共 80 列).一个\t 就跳到下一个大
表示,已经被 printf 输出的字符个数的.
再来看看 case &#39;s&#39;: case &#39;s&#39;: chptr = va_arg (ap, unsigned char*); i = 0; while (chptr [i]) { cnt_printed_chars++; putchar (chptr [i++]); } break; 和 case &#39;c&#39;,同出一辙.cnt_printed_chars++放在了循环内,也证明 了刚才提到的他的作用.另外我们也看到了 cnptr 是用来在处理字符串时的位置 指针.到此为止,我们清楚的所有变量的用途,前途变得更加光明了.
break; case &#39;\t&#39; : printChar (32); printChar (32); /* 32 = space */ printChar (32); printChar (32); printChar (32); printChar (32); printChar (32); printChar (32); break; case &#39;\b&#39;: backspace (); break; case &#39;\a&#39;: beep (); break; default : printChar ((unsigned char) c); } return c; }
用上 switch-case 了. 快速浏览一下下面的代码.
首先看看 case &#39;c&#39;的部分
case &#39;c&#39;: putchar (va_arg (ap, unsigned char)); cnt_printed_chars++; break;
%c 表示仅仅输出一个字符.因此先通过 va_arg 进行参数的类型转换,之后用 putchar[1]输出到屏幕上去.之后是 cnt_printed_chars++,通过这句我们就可以判断出 cnt_printed_chars 使用来
开门见山,上来就当前字符是否办断是否%.一猜就知道如果成立 pos++马上取 出下一个字符在 d,f,l 等等之间进行判断.往下一看,果真不出所料:
相关文档
最新文档