老师未批改
1. 分别用%d %o %x 输出一个int值, 并写清楚区别.
代码如下:
/*************************************************************************
> 文件名: homework01.c
> 作者: 花心胡萝卜
> 邮箱: hxhlb@hxcarrot.com
> 创建时间: 2016-09-06 23:22:57
************************************************************************/
#include<stdio.h>
int main() {
// 定义一个整数用来显示.
int iValue = 100;
// 使用%x输出
printf("使用%%x输出的结果是:[%x]\n", iValue);
// 使用%o输出
printf("使用%%o输出的结果是:[%o]\n", iValue);
// 使用%d输出
printf("使用%%d输出的结果是:[%d]\n", iValue);
// 暂停作用
getch();
return 0;
}运行效果如下:
%d %o %x 的区别:
%d就是十进制数字.
%o 则是八进制的数字.
%x 则是十六进制的数字, 使用小写字母输出.
2. 将printf的帮助文档看一遍, 将里边的例子运行一遍. 注意C11新增的函数, printf 和 printf_s的区别.
printf, fprintf, sprintf, snprintf, printf_s, fprintf_s
Defined in header <stdio.h>
(1) int printf( const char *format, ... ); (until C99)
int printf( const char *restrict format, ... ); (since C99)
(2) int fprintf( FILE *stream, const char *format, ... ); (until C99)
int fprintf( FILE *restrict stream, const char *restrict format, ... ); (since C99)
(3) int sprintf( char *buffer, const char *format, ... ); (until C99)
int sprintf( char *restrict buffer, const char *restrict format, ... ); (since C99)
(4) int snprintf( char *restrict buffer, int bufsz,
const char *restrict format, ... ); (since C99)
(5) int printf_s(const char *restrict format, ...); (since C11)
(6) int fprintf_s(FILE *restrict stream, const char *restrict format, ...); (since C11)
(7) int sprintf_s(char *restrict buffer, rsize_t bufsz,
const char *restrict format, ...); (since C11)
(8) int snprintf_s(char *restrict buffer, rsize_t bufsz,
const char *restrict format, ...); (since C11) Loads the data from the given locations, converts them to character string equivalents and writes the results to a variety of sinks.
1) Writes the results to the output stream stdout.
2) Writes the results to the output stream stream.
3) Writes the results to a character string buffer. The behavior is undefined if the string to be written (plus the terminating null character) exceeds the size of the array pointed to by buffer.
4) Writes the results to a character string buffer. At most bufsz - 1 characters are written. The resulting character string will be terminated with a null character, unless bufsz is zero. If bufsz is zero, nothing is written and buffer may be a null pointer, however the return value (number of bytes that would be written) is still calculated and returned.
5-8) Same as (1-4), except that the following errors are detected at runtime and call the currently installed constraint handler function:
· the conversion specifier %n is present in format
· any of the arguments corresponding to %s is a null pointer
· format or buffer is a null pointer
· bufsz is zero or greater than RSIZE_MAX
· encoding errors occur in any of string and character conversion specifiers
· (for sprintf_s only), the string to be stored in buffer (including the trailing null) would be exceed bufsz
As all bounds-checked functions, printf_s, fprintf_s, sprintf_s, and snrintf_s are only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including <stdio.h>.翻译:
第一段列出了8个函数, 都注明了C语言标准的出处.
第二段, 写明了这几个函数的区别.1) 将结果写入到
标准输出流中.
2) 将结果写入到文件输出流中.
3) 将结果写入一个字符串缓冲区中.
需要注意的是,如果被写入的源字符串(包含终止符\0)的长度大于目标字符串缓冲区的长度, 将会发生缓冲区溢出, 我们称之为未定义的行为.
4) 将结果写入一个字符串缓冲区中.
该函数最多写入bufsz - 1个字符进入字符串缓冲区.
如果bufsz不是0的话, 缓冲区内的最终字符串会以null字符('0')结束.
如果bufsz是0,将不会写入任何数据, 目标字符串缓冲区可能会是一个空指针.
但是该函数的返回值需要注意, 如果源字符串长度超过了bufsz的长度, 那么返回值仍然是将要写入的字符的长度, 而不是写入的长度.
举例:/************************************************************************* > 文件名: snprintfTest.c > 作者: 花心胡萝卜 > 邮箱: hxhlb@hxcarrot.com > 创建时间: 2016-09-07 10:30:51 ************************************************************************/ #include<stdio.h> int main() { char a[16]; size_t i; i = snprintf(a, 13, "%012d", 33333); printf("i = %d, a = %s\n", i, a); i = snprintf(a, 9, "%012d", 55555); printf("i = %d, a = %s\n", i, a); return 0; }
![]()
看看我们的运行结果, 不同的编译器对标准的支持不同. 可能是我的TCC比较老版本了.
5-8) 基本等同于 1) -- 4), 不同的是, 在运行时检测以下错误, 并在调用时检测方法约束.
· 当存在格式控制符(转换说明符)%n的时候
· 当%s的任意一个参数是空指针的时候
· 当格式控制符或目标字符串缓冲区是空指针的时候.
· 当bufsz是0或者大于RSIZE_MAX定义的时候(超长)
· 在任意一个字符串或字符转换时发生编码错误的时候(转换类型不匹配).
· 要存储在缓冲区的字符串(包括结束符'0')的长度超过bufsz的时候.(只有 sprintf_s 方法)
所有的边界检查函数(printf_s, fprintf_s, sprintf_s, snprintf_s) 只有在__STDC_LIB_EXT1__宏在执行的时候被定义, 并且用户在代码#include <stdio.h>之前, 自定义宏__STDC_WANT_LIB_EXT1__为常量数字1(#define STDC_WANT_LIB_EXT1 1)的时候, 才保证可使用.
============================================================================================================================================================
从本分割线开始, 到下面的分割线结束, 请暂时不要看...
下面我们就来试试上边说的这些.首先看看我们的测试代码
/*************************************************************************
> 文件名: boundsChecked.c
> 作者: 花心胡萝卜
> 邮箱: hxhlb@hxcarrot.com
> 创建时间: 2016-09-07 11:58:44
************************************************************************/
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>
#include <stdint.h>
int main() {
// 测试是否定义了__STDC_LIB_EXT1__
#ifdef __STDC_LIB_EXT1__
printf("已经定义了__STDC_LIB_EXT1__\n");
#else
printf("还没有定义__STDC_LIB_EXT1__\n");
#endif
// 测试%n
int iOutputCount = 0;
printf("%s%n\n", "Hello Hades!", &iOutputCount);
printf_s("%s%n\n", "Hello Hades!", &iOutputCount);
// 测试%s空指针
char* nullPtr = NULL;
printf("%s%s\n", "Hello", nullPtr);
printf_s("%s%s\n", "Hello", nullPtr);
// 测试格式字符串为NULL
char strTestBuf1[20] = { 0 };
sprintf(strTestBuf1, "Hello Hades!");
sprintf_s(strTestBuf1, 13, "Hello Hades!");
// 测试缓冲区为NULL
char* strTestBuf2 = NULL;
sprintf(strTestBuf2, "%s", "Hello Hades!");
sprintf_s(strTestBuf2, 13, "%s", "Hello Hades!");
// 测试bufsz==0 或 > RSIZE_MAX
char strTestBuf3[20] = { 0 };
sprintf_s(strTestBuf3, 0, "Hello Hades!");
sprintf_s(strTestBuf3, SIZE_MAX + 1, "Hello Hades!");
// 测试格式化字符编码错误
printf("%d%s\n", 333, 222);
printf_s("%d%s\n", 333, 222);
// 测试sprintf_s要存储的字符串>规定的长度
char strTestBuf4[20] = { 0 };
sprintf_s(strTestBuf4, 5, "Hades");
return 0;
}注:
测试代码不一定是正确的.
从本分割线以上到上一条分割线请先不要看....
============================================================================================================================================================在测试 %n 的时候, 我还发现了一个问题.
如下图:![]()
VS2015编译的程序竟然出错了! 难道是不支持%n?
经过多方搜索, 确定需要改成如下方法执行:![]()
这是因为%n不安全, 微软编译器默认关闭了%n的功能.
结论
不同之处你发现了吗?
我们的printf_s函数更加的安全有保障, 防止有意/无意/恶意的缓冲区溢出.3. printf 详解笔记.
笔记连接:请点我查看
4. 厘米转英尺的程序编写, CM2Ft, 输出100次转换, 前面保留3位, 不足补零, 小数5位, 不保留0.
编写程序代码如下:
/*************************************************************************
> 文件名: homework04.c
> 作者: 花心胡萝卜
> 邮箱: hxhlb@hxcarrot.com
> 创建时间: 2016-09-06 23:37:25
************************************************************************/
#include<stdio.h>
#define ALL_LEN 9 // 所有长度, 根据题目, 前边保留3位, 小数保留5位, 总长度9位
#define DOT_LEN 5 // 小数精度, 题目规定为5
/**
* 获取一个double类型的小数部分, 在指定精度内最后共有几个0.
* 最多判断32位小数.
*
* 参数:
* dValue 小数
* iLen 小数精度
*
* 返回值:
* >= 0 返回小数最后0的个数
* 超出精度范围 返回-1
*
*/
int lastNumZeroCount(double dValue, int iLen) {
// 去掉整数部分
double dTmp = dValue - (int)dValue;
// 判断小数精度
if (iLen > 32) return -1;
// 初始化临时的字符数组, 用来判定最后一位是否是0的
char cTmp[35] = { '\0' };
sprintf(cTmp, "%.*g", iLen, dTmp);
int iCount = 0;
// 判断小数位数
// 这里其实可以使用string.h中的函数 strlen
// 但是还是自己实现一下
for (int i = 0; i < 35; i++) {
if (cTmp[i] != '\0') {
iCount++;
} else {
break;
}
}
// 判断最后为0的个数
// 首先 iCount-2 是为了去掉 "0." 这两个字符的影响
// %g 会去掉最后的0
return iLen - iCount + 2;
}
int main() {
// 定义厘米为1
int cmBase = 1;
// 定义英尺为 1/30.48
// 因为 1英尺 ≈ 30.48厘米
double ftBase = 1 / 30.48;
printf("\t厘米\t\t英尺\t\n");
for (int i = 1; i <= 100; i++) {
int iResult = lastNumZeroCount(ftBase * i, DOT_LEN);
iResult = iResult < 0 ? 0 : iResult;
printf("\t%d\t\t%0*.*f\t\n",
cmBase * i,
ALL_LEN - iResult,
DOT_LEN - iResult,
ftBase * i);
}
return 0;
}运行效果如下:
5. 米转英尺, 要求同上.
编写程序代码如下:
/*************************************************************************
> 文件名: homework05.c
> 作者: 花心胡萝卜
> 邮箱: hxhlb@hxcarrot.com
> 创建时间: 2016-09-06 23:37:33
************************************************************************/
#include<stdio.h>
#define ALL_LEN 9 // 所有长度, 根据题目, 前边保留3位, 小数保留5位, 总长度9位
#define DOT_LEN 5 // 小数精度, 题目规定为5
/**
* 获取一个double类型的小数部分, 在指定精度内最后共有几个0.
* 最多判断32位小数.
*
* 参数:
* dValue 小数
* iLen 小数精度
*
* 返回值:
* >= 0 返回小数最后0的个数
* 超出精度范围 返回-1
*
*/
int lastNumZeroCount(double dValue, int iLen) {
// 去掉整数部分
double dTmp = dValue - (int)dValue;
// 判断小数精度
if (iLen > 32) return -1;
// 初始化临时的字符数组, 用来判定最后一位是否是0的
char cTmp[35] = { '\0' };
sprintf(cTmp, "%.*g", iLen, dTmp);
int iCount = 0;
// 判断小数位数
// 这里其实可以使用string.h中的函数 strlen
// 但是还是自己实现一下
for (int i = 0; i < 35; i++) {
if (cTmp[i] != '\0') {
iCount++;
} else {
break;
}
}
// 判断最后为0的个数
// 首先 iCount-2 是为了去掉 "0." 这两个字符的影响
// %g 会去掉最后的0
return iLen - iCount + 2;
}
int main() {
// 定义米为1
int mBase = 1;
// 定义英尺为 1/0.3048
// 因为 1英尺 ≈ 0.3048米
double ftBase = 1 / 0.3048;
printf("\t米\t\t英尺\t\n");
for (int i = 1; i <= 100; i++) {
int iResult = lastNumZeroCount(ftBase * i, DOT_LEN);
iResult = iResult < 0 ? 0 : iResult;
printf("\t%d\t\t%0*.*f\t\n",
mBase * i,
ALL_LEN - iResult,
DOT_LEN - iResult,
ftBase * i);
}
return 0;
}运行效果如下:
作业4,5扩展
既然他们代码这么相似, 那何不合并到一起, 根据参数来判定呢?修改后的代码如下:
/*************************************************************************
> 文件名: G:\_DEV\VS_Proj\VS2015_Proj\_C++VIP\lesson8\homework06.c
> 作者: 花心胡萝卜
> 邮箱: hxhlb@hxcarrot.com
> 创建时间: 2016-09-07 0:47:17
************************************************************************/
#include<stdio.h>
#define ALL_LEN 9 // 所有长度, 根据题目, 前边保留3位, 小数保留5位, 总长度9位
#define DOT_LEN 5 // 小数精度, 题目规定为5
/**
* 获取一个double类型的小数部分, 在指定精度内最后共有几个0.
* 最多判断32位小数.
*
* 参数:
* dValue 小数
* iLen 小数精度
*
* 返回值:
* >= 0 返回小数最后0的个数
* 超出精度范围 返回-1
*
*/
int lastNumZeroCount(double dValue, int iLen) {
// 去掉整数部分
double dTmp = dValue - (int)dValue;
// 判断小数精度
if (iLen > 32) return -1;
// 初始化临时的字符数组, 用来判定最后一位是否是0的
char cTmp[35] = { '\0' };
sprintf(cTmp, "%.*g", iLen, dTmp);
int iCount = 0;
// 判断小数位数
// 这里其实可以使用string.h中的函数 strlen
// 但是还是自己实现一下
for (int i = 0; i < 35; i++) {
if (cTmp[i] != '\0') {
iCount++;
} else {
break;
}
}
// 判断最后为0的个数
// 首先 iCount-2 是为了去掉 "0." 这两个字符的影响
// %g 会去掉最后的0
return iLen - iCount + 2;
}
/**
*
* 显示程序用法
*
* 参数:
* main函数的argv参数
*
*/
int showUsage(char* argv[]) {
printf("用法: %s cm | m\n区分大小写.\n", argv[0]);
// 退出
return 0;
}
int main(int argc, char* argv[]) {
// 定义米为1
int mBase = 1;
// 定义厘米为1
int cmBase = 1;
// 定义米的英尺为 1/0.3048
// 因为 1英尺 ≈ 0.3048米
double ftMBase = 1 / 0.3048;
// 定义厘米的英尺为 1/30.48
double ftCmBase = 1 / 30.48;
if (argc < 2) {
showUsage(argv);
exit(0);
}
if (0 == strcmp("cm", argv[1])) {
printf("\t厘米\t\t英尺\t\n");
for (int i = 1; i <= 100; i++) {
int iResult = lastNumZeroCount(ftCmBase * i, DOT_LEN);
iResult = iResult < 0 ? 0 : iResult;
printf("\t%d\t\t%0*.*f\t\n",
mBase * i,
ALL_LEN - iResult,
DOT_LEN - iResult,
ftCmBase * i);
}
} else if (0 == strcmp("m", argv[1])) {
printf("\t米\t\t英尺\t\n");
for (int i = 1; i <= 100; i++) {
int iResult = lastNumZeroCount(ftMBase * i, DOT_LEN);
iResult = iResult < 0 ? 0 : iResult;
printf("\t%d\t\t%0*.*f\t\n",
mBase * i,
ALL_LEN - iResult,
DOT_LEN - iResult,
ftMBase * i);
}
} else {
showUsage(argv);
exit(0);
}
return 0;
}运行效果如下:
如有错误,请提出指正!谢谢.
本文由 花心胡萝卜 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2016-10-06 at 03:02 pm




虽能达到效果,但代码写得有些多,繁重。只要能达到同样效果,我觉得代码尽量往简单,少代码去写,因为以后工作是一个团队,别人浏览自己代码会轻松很多!!提高工作效率!!
关键是没想到什么好的解决方案, 不知道您是不是能分享一下?
你这样太麻烦了吧,,直接对齐感觉就可以了。
for (int i = 1; i <= 100; i++)
{
}
题目好像还要求小数点后有五位,加个控制位数的,刚才看到有位数要求。
题目还要求
最后的0去掉~~用
%g的话, 0是会被去掉的, 但是前面保留3位就变成了4位.我没想到很好的解决方案.