如果你懂 C++,你就差不多懂 C 了。使用 C 的经验给了你另一个吹牛的机会——一个字符长,所以应该合适!–在你的简历上。c 是操作系统和嵌入式系统的流行语言,里面有很多库。
c 基本上是我们在上课前所学的内容,不包括
-
SDL/SSDL
-
cin
和cout
-
&
参数 -
bool
(用int
代替) -
constexpr
(用const
代替)
没有类、异常、重载运算符、模板或命名空间。存在,但没有成员函数或公共/私有/受保护部分(都是公共的)。
还有一些较小的差异,包括以下几点:
-
铸造长这样,
(int) f
,不是这样:int (f)
。 -
struct
S {...};
确实声明了一个名为S
的struct
,但是声明该类型的变量需要一个额外的单词:struct
S myStruct;
cplusplus.com 和 cppreference.com,不管名字如何,都是 C 和 C++ 的好资源。
在 Visual Studio 中,你不能选择“C 文件”作为添加到你的项目中的东西,但是你可以选择“C++ 文件”并将你的命名为<something>.c
。编译器会将其视为 C 文件。照常编译和运行。
在 Unix 或 MinGW 中,你可以将你的程序命名为<something>.c
并用gcc
命令进行编译,这就像g++
只适用于 C 文件。示例代码将这一点构建到了它的 Makefiles 中。
这是必须的“你好,世界!”
// Hello, world! -- again! This time in C.1
// -- from _C++20 for Lazy Programmers_
#include <stdio.h>
int main ()
{
printf ("Hello, world!\n");
return 0;
}
Example 28-1“Hello, world!” in C
一些需要注意的事项:
-
Includ e 文件,无论是否属于系统,都以
.h
结尾。那些从 C 继承而来的 C++ 将首字母c
去掉了:stdlib.h
而不是cstdlib
;math.h
不是cmath
。 -
我们使用
printf
打印–下一节将详细介绍。
Extra
如果您希望在同一个项目中同时包含 C++ 和 C 文件,这是可行的,但是需要一些技巧。您需要将main
放在一个 C++ 文件中,并且对于要在 C++ 文件中使用的任何 C include 文件,这样包装 include:
extern "C"
{
#include "mycheader.h"
}
如果你用的是 gcc/g++,那就用 g++ 链接。
关于这方面的更多内容,在写作的时候,参见 C++ 超级 FAQ, isocpp.org/wiki/faq/mixing-c-and-cpp
。
所有这些 I/O 功能都在stdio.h
中。
代替cout >>
,C 有函数printf
(print-f,意为“带格式打印”):
printf ("Ints like %d, strings like %s, and floats like %f -- oh, my!\n",
12, "ROFL", 3.14159); // %d is for "decimal"
将打印
Ints like 12, strings like ROFL, and floats like 3.141590 -- oh, my!
%
序列是“格式字符串”("Ints like %d..."
)中的占位符,显示每个连续参数的位置。你可以有尽可能多的论点。最常见的%
序列有%d
表示十进制整数、%f
表示固定浮点、%s
表示字符串,即字符数组。%%
的意思是“仅仅是%
字符。”
你可以把修饰语放在里面。例如,%.2f
将小数点右边的两位数字。
scanf
(“扫描-f”)取代了cin >>
,如下所示:
scanf ("%f %s", &myDouble, myCharArray);
&
的意思是“记下…的地址”C 和 C++ 都有这个操作符,但是 C 一直在用。scanf
需要知道myDouble
在哪里,这样它就可以修改它(下一节将详细介绍)。它不需要myCharArray
的地址,因为myCharArray
T5 是地址。
如果你使用本章中的scanf
或其他一些函数,Visual Studio 会给出一个警告,告诉你这个函数是不安全的,就像使用一些cstring
函数一样(见第 14 章)。如果您想禁用警告,请将此行放在main
之前:
#pragma warning (disable:4996)
示例 28-2 演示printf
和scanf
。
// Program to test C's major standard I/O functions
// -- from _C++20 for Lazy Programmers_
#include <stdio.h>
int main ()
{
float number; // number we'll read in and print out
int age; // your age
enum {MAXSTR = 80};2 // array size
char name [MAXSTR]; // your name
// A printf showing float, and use of % sign
printf ("%3.2f%% of statistics are made up on the spot!\n\n",
98.23567894);
// printfs using decimal, hex, and char array
// %02d means pad number to a width of 2 with leading 0's
printf ("%d is 0x%x in hexadecimal.\n\n", 16, 16);
printf ("\"%s\" is a $%d.%02d word.\n\n", "hexadecimal",
5, 0);
// scanf needs & for the variables it sets
printf ("Enter a floating-point number: ");
scanf ("%f", &number);
printf ("%g is %f in fixed notation and %e in scientific.\n",
number, number, number);
printf ("...in scientific with a precision
of 2: %.2e.\n\n",
number);
// ...except arrays, since they're already addresses
printf ("Enter your name and age: ");
scanf ("%s %d", name, &age);
printf ("%s is %d years old!\n\n", name, age);
return 0;
}
Example 28-2printf and scanf
输出可能是这样的:
98.24% of statistics are made up on the spot!
16 is 0x10 in hexadecimal.
"hexadecimal" is a $5.00 word.
Enter a floating-point number: 2
2 is 2.000000 in fixed notation and 2.000000e+000 in scientific.
Here it is in scientific with a precision of 2: 2.00e+000.
Enter your name and age: Linus 7
Linus is 7 years old!
表 28-1 包含 printf 和 scanf 格式代码的部分列表。有关更多详细信息,请参见(在编写本报告时)cplusplus . com/reference/CST dio/printf/和cplusplus . com/reference/CST dio/scanf/。
C 中的文件 I/O 使用了printf
和scanf
的变体。考虑以下代码:
FILE* file = fopen ("newfile.txt", "w"); // open file
if (!file) { printf("Can't open newfile.txt!\n"); return 0; }
// did it work? if not, quit main
fprintf (file, "Avagadro's number is %.4e.\n", 6.023e+023);
// use it
fclose (file); // close it
为了打开文件进行写入,我们调用fopen
(“f-open”),给它一个文件名,"w"
表示“写入”(到一个输出文件)。文件信息存储在一个类型为FILE*
的指针中,如果fopen
失败的话,这个指针就是NULL
,相当于 C++ 的nullptr
。
关闭文件只是将文件指针发送到fclose
,如图所示。
在这两者之间,通过添加file
作为第一个参数,将printf
修改为fprintf
。
如果你想读而不是写,打开文件,用"r"
表示“读”,并类似地修改scanf
:
file = fopen ("newfile.txt", "r");
if (!file) { printf("Can't open newfile.txt!\n"); return 0; }
fscanf (file, "%s %s %s %e", word1, word2, word3, &number);
fclose (file);
如果成功,fscanf
和scanf
返回您给出的参数个数。如果号码不一样,那就是出了问题。可能您已到达文件末尾。诸如此类测试:
while (1) // while true
{
if (fscanf (file, "%d", number) != 1)
/* it didn't work -- handle that */;
//...
}
示例 28-3 演示了这些功能。
// Program to test C's major standard I/O functions
// -- from _C++20 for Lazy Programmers_
#include <stdio.h>
int main ()
{
FILE* file; // a file to write to or read from
float number; // number we'll read in and print out
enum { MAXSTR = 80 }; // array size
char junk [MAXSTR]; // a char array for reading in (and thus
// discarding) a word
// printing to file. The number gets 4 digits of precision
file = fopen ("newfile.txt", "w");
if (!file)
{
printf ("Can't open newfile.txt for writing!\n"); return 0;
}
printf ( "Avagadro's number is %.4e.\n", 6.023e+023);
fprintf (file, "Avagadro's number is %.4e.\n", 6.023e+023);
fclose (file);
// reading from a file
file = fopen ("newfile.txt", "r");
if (!file)
{
printf("Can't open newfile.txt for reading!\n"); return 0;
}
fscanf (file, "%s %s %s %e", junk, junk, junk, &number);
// Read in 3 words, then the number we want
fclose (file);
printf ("Looks like Avagadro's number is still %.4e.\n", number);
return 0;
}
Example 28-3fprintf, fscanf, fopen, and fclose
该文件将包含Avagadro's number is 6.0230e+023.
,屏幕输出将为:
Avagadro's number is 6.0230e+023.
Looks like Avagadro's number is still 6.0230e+023.
这里还有几个 I/O 函数。
-
sscanf
**(“s-scan-f”)**从字符数组中读取。如果myCharArray
是"2.3 kg"
,我们可以说sscanf (myCharArray, "%f %s", &myDouble, myWord); // myDouble gets 2.3, myWord gets "kg"
,在 C++ 中我们会说
sscanf (myCharArray, "%f %s", &myDouble, myWord); // myDouble gets 2.3, myWord gets "kg"
-
sprintf
打印到一个字符数组:sprintf (myCharArray, "%s %f", name, number);
在 C++ 中我们会说
sstream myStringStream; myStringStream << name << number; string myString = myStringStream.str();
-
fgets
从文件中读取一行文本:fgets (myCharArray, MAX_STRING, someFile); // read myCharArray, // which should // be no more than // MAX_STRING long, // from someFile
或者键盘:
fputs
打印一个char
数组到一个文件:
fgets (myCharArray, MAX_STRING, stdin);
fputs (myCharArray, someFile);
您可以将该文件命名为stdout
,但通常我们会使用一个更短的版本:
puts
(myCharArray); //sends to stdout
我们不会将fgets
缩写为gets
: gets
存在,但被认为是不安全的,行为不像fgets
,因此不被使用。
示例 28-4 演示了这些功能。
表 28-1
printf
和scanf
的格式代码
%序列
|
意义
|
| --- | --- |
| %d
| 十进制格式的整数。 |
| %o
| 无符号八进制整数(基数为 8)。 |
| %x/%X
| 无符号十六进制整数(基数为 16)。十六进制 1f 将显示为0x1f/0X1F
。 |
| %c
| 性格。 |
| %s
| 字符数组。 |
| %f
| 定点浮点。 |
| %e/%E
| 科学记数法浮点。如果你说%E
,那么E
将是大写的。 |
| %g/%G
| 默认浮点。如果你说%G
,那么E
(如果有的话)将会是大写的。 |
| %p
| 指针。 |
| %%
| %
人物本身。 |
// Program to test sprintf, sscanf, fgets, fputs, puts
// -- from _C++20 for Lazy Programmers_
#include <stdio.h>
int main ()
{
while (1) // forever, or until break...
{
enum {MAXLINE=256}; // array size for line
char line [MAXLINE]; // a line of text
enum {MAXSTR = 80}; // array size for word
char word [MAXSTR]; // your word
int number; // a number to read in
// get an entire line with gets; on end of file quit
printf("Enter a line with 1 word & 1 number, end of file to quit: ");
if (! fgets (line, MAXLINE, stdin)) break;
// repeat line with fputs
printf("You entered: ");
fputs (line, stdout); // You *can* use fputs with stdout; puts is // more usual
// Use char array as source for 2 arguments
if (sscanf (line, "%s %i", word, &number) != 2)
puts ("That wasn't a word and a number!\n");
else
{
// Print using sprintf and puts
sprintf(line, "The name was %s and the number was %i.\n",
word, number);
puts (line);
// If this weren't a demo of new functions, I'd say
// printf ("The name was %s and the number was %f.\n",
// name, number);
}
}
puts ("\n\nBye!\n");
return 0;
}
Example 28-4A program using sprintf, sscanf, fgets, and so on
样本输出:
Enter a line with 1 word and 1 numbers, end of file to quit: Mila 18
You entered: Mila 18
The name was Mila and the number was 18.
Enter a line with 1 word and 1 numbers, end of file to quit: Catch 22
You entered: Catch 22
The name was Catch and the number was 22.
Enter a line with 1 word and 1 numbers, end of file to quit: [Enter Ctrl-D or Ctrl-Z here]
Bye!
在表 28-2 中描述的函数中,如果我没有给出函数返回的含义,那是因为我们很少关心那个函数。有了fopen
、fgets
和scanf
家族,我们做到了。
表 28-2
C 语言中常见的stdio
函数
printf
和变体
| |
| --- | --- |
| int``printf
T2】...);
| 根据formatString
的规定,在formatString
之后打印屏幕参数。 |
| int``fprintf
T2】const char* formatString, ...);
| 与printf
相同,但打印到file
。 |
| int``sprintf
T2】const char* formatString, ...);
| 与printf
相同,但打印到str
。 |
| scanf
和变体 | |
| int``scanf
T2】...);
| 按照formatString
的指定,读取formatString
之后的参数。如果在读取任何文件之前到达EOF
,则返回EOF
(文件结束);成功读取的 else #个参数。 |
| int``fscanf
T2】const char* formatString, ...);
| 与scanf
相同,但从file
读取。 |
| int``sscanf
T2】const char* formatString, ...);
| 与scanf
相同,但从str
读取 |
| 打开/关闭文件 | |
| FILE*``fopen
T2】const char* fileMode);
| 打开由filename
指定的文件并返回指针。常见的fileMode
有"r"
(读)"w"
(写)"a"
(追加)。 |
| int``fclose
T2】 | 关闭文件。 |
| 读/写字符串 | |
| int``puts
T2】int``fputs
T2】FILE* file);
| 打印str
/打印str
到file
。 |
| char*``fgets
T2】FILE* file);
| 从file
(可能是stdin
)读取str
,最多读取max-1
个字符(所以str
的大小应该是max
或者更大)。失败时返回NULL
。 |
-
scanf
因缺少&:
而失败scanf ("%f %s", myDouble, myCharArray);
应该是
scanf ("%f %s",``&
T2】很容易忘记
&
,编译器可能不会警告你。
C 没有&
参数,但和 C++ 一样,它认为一次函数调用不会改变其他参数。哦哦。
void swap (int arg1, int arg2)
{
int temp = arg1; arg1 = arg2; arg2 = temp;
}
int main ()
{
int x, y;
...
swap (x, y); // x, y will not be changed
...
}
c 希望您发送参数的地址:
int main ()
{
int x, y;
...
swap (&
x, &y); // x's and y's addresses are sent, not x and y
...
}
该函数获取该地址,并使用*
来引用它所指向的东西,其中可以被改变:
void swap (int* arg1, int* arg2)
{
int temp = *arg1; *arg1 = *arg2; *arg2 = temp;
}
有效!但是它很笨重,并且引入了一个令人恼火的常见错误:忘记*
的。
示例 28-5 展示了一个使用它的程序(并且不要忘记*
)。
// Program to do statistics on some strings
// from _C++20 for Lazy Programmers_
#include <stdio.h> // for printf, scanf
#include <string.h> // for strlen
void updateLineStats (char line[], unsigned int* length,
float* averageLineLength);
int main ()
{
printf ("Type in a line and I'll reply. ");
printf ("Type the end-of-file character to end.\n");
while (1) // forever (or until a break) ...
{
enum { MAXSTRING = 256 }; // max line length
char line [MAXSTRING]; // the line
int length; // its current length
float averageLineLength;
// get line of input
if (!fgets (line, MAXSTRING, stdin)) break;
// do the stats. We send addresses, not variables, using &
updateLineStats (line, &length, &averageLineLength);
// give the result
printf ("Length of that line, ");
printf ("and average so far: %d, %.2f.\n",
length, averageLineLength);
}
return 0;
}
void updateLineStats (char line[], unsigned int* length,
float* averageLineLength)
{
static int totalLinesLength = 0; // have to remember these
static int linesSoFar = 0; // for next time
// length is a pointer, so *length is the length
*length = (unsigned int) strlen (line);
// casting from size_t to unsigned int
// fgets included the final \n, but I won't count that:
--(*length);
++linesSoFar;
totalLinesLength += *length;
*averageLineLength = // and averageLineLength needs its *, too
totalLinesLength / ((float) linesSoFar);
}
Example 28-5Program using parameter passing with *’s in C
示例会话:
Type in a line and I'll reply. Type the end-of-file character to end.
alpha
Length of that line, and average so far: 5, 5.00.
bet
Length of that line, and average so far: 3, 4.00.
soup
Length of that line, and average so far: 4, 4.00.
- **" <变量>的间接级别不同"或"无法从<类型>转换为<类型> * "或"在不强制转换的情况下从整数生成指针。"**有各种各样的抱怨方式,但底线是很难记住在函数调用中放入
&
的,更难记住*
的 *every!时间到了!*你使用传入的变量。我从未找到解决办法。至少你知道这可能是出错的原因。
忘记new
、new []
、delete
和delete []
。c 的动态内存更简单,虽然更难看:
#include <stdlib.h> // for malloc, free
...
someType* myArray = malloc (myArraySize * sizeof (someType));
// allocate a myArraySize element array of some type
...use the array...
free (myArray); // throw it back
没有析构函数来帮助你记住释放东西——你只能靠自己了。
示例 28-6 将示例 14-3(一个使用动态数组的早期程序)改编为 c。
// Program to generate a random passcode of digits
// -- from _C++20 for Lazy Programmers_
#include <stdio.h>
#include <stdlib.h> // for srand, rand, malloc, free
#include <time.h> // for time
int main ()
{
srand ((unsigned int) time(NULL));// start random # generator
// NULL, not nullptr
int codeLength; // get code length
printf ("I'll make your secret passcode. "
"How long should it be? ");
scanf ("%d", &codeLength);
// allocate array
int* passcode = malloc (codeLength * sizeof(int));
for (int i = 0; i < codeLength; ++i)// generate passcode
passcode[i] = rand () % 10; // each entry is a digit
printf ("Here it is:\n"); // print passcode
for (int i = 0; i < codeLength; ++i)
printf ("%d", passcode[i]);
printf ("\n");
printf ("But I guess it's not secret any more!\n");
free (passcode); // deallocate array
return 0;
}
Example 28-6A C program using dynamic memory
Exercises
做第 13 章和第 14 章的练习,不包括那些用 SSDL 的。
Footnotes [1](#Fn1_source)如果你的编译器不理解像这样的注释//——“c++ 风格的注释”——改为注释/*
C-style */
。或者获得更新的编译器。
“enum
hack”是声明MAXSTR
的一种方式。它比const int MAXSTR=80
需要更少的输入(以及内存和运行时间);。