strcasecmp() is the case-insensitive version of strcmp(). When building, make sure to include strings.h (note the plural form).
strncasecmp() is the counterpart to strncmp().
eg.,
..
#include <string.h>
void main(int argc, char** argv)
..
printf("\n\"%s\" and \"%s\" are ", argv[1], argv[2]);
strcasecmp(argv[1], argv[2]) ? printf(" .. not identical") : printf(" .. identical");
..
% ./strcompare next NeXT
"next" and "NeXT" are .. identical
[2] Initializing a Variable Length Array
As of the length of the variable length array is not known to the compiler at compile time, initializing a variable length automatic array with some default value usually results in a compilation error.
eg.,
% cat -n varlen.c
1 #include <stdlib.h>
2
3 void main(int argc, char** argv) {
4 int size=atoi(argv[1]);
5 int array[size] = { 0 };
6 }
/home/gmandali/C % cc -o varlen varlen.c
"varlen.c", line 5: variable length array can not be initialized: array
cc: acomp failed for varlen.c
One option is to explicitly initialize each element in the array to the desired default value.
eg.,
% cat -n varlen.c
1 #include <stdlib.h>
2
3 void main(int argc, char** argv) {
4 int size=atoi(argv[1]);
5 int array[size];
6 for (int i = 0; i < size; ++i)
7 array[i] = 0;
8 }
% cc -o varlen varlen.c
%
In this case, sizeof operator evaluates the operand (variable length array).
[3] Check if a Process is Alive
kill() function can be used to send a null signal (signal 0) to check the validity of a process.
eg.,
% cat -n chkproc.c
1 #include <sys/types.h>
2 #include <signal.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5
6 void main(int argc, char **argv) {
7 pid_t pid = atoi( argv[1] );
8 int rc = kill( pid, 0 );
9 if ( !rc ) {
10 printf("process with pid ( %d ) is alive", pid);
11 } else {
12 printf("process with pid ( %d ) is not found", pid);
13 }
14 }
% cc -o chkproc chkproc.c
% ps
PID TTY TIME CMD
19846 pts/25 0:00 ps
28978 pts/25 0:00 bash
% ./chkproc 28978
process with pid ( 28978 ) is alive
% ./chkproc 28979
process with pid ( 28979 ) is not found
pid_t is actually int data type.
[4] Evaluating "( integer )" Expression
This is a simple one but probably an important one to remember. When dealing with an expression that yields an integer or when testing an integer, only a value of zero evaluates to false. Everything else
including negative numbers, floating point numbers, characters, strings, .. are evaluated to true. In other words, anything non-zero is true.
[5] Variable Declaration in a switch { case: } Statement
Let's start with an example that shows compilation failure.
% cat -n varincase.c
1 #include
2
3 void main() {
4 switch ( 1 ) {
5 case 2:
6 int i = 15;
7 break;
8 default:
9 printf("default\n");
10 }
11 }
% cc -o varincase varincase.c
"varincase.c", line 6: syntax error before or at: int
"varincase.c", line 7: undefined symbol: i
cc: acomp failed for varincase.c
In this example, identifier i is being declared and initialized in the case label of switch statement. Since no linkage or no static scope was specified,
identifier i will have automatic storage duration that is local to the block containing the invocation (switch() { .. }, that is).
However the problem with this code is that case <expression> and default are labels and the controlling expression in switch statement causes the control to jump to
appropriate label. In the example, when the control is transferred to default label, it enters the scope of identifier i without initializing it [as it bypasses the case
where the identifier is being declared and initialized], which is not permitted. Therefore the fix is to declare the identifier (if at all needed) in its own compound statement so the scope is limited to that
compound statement. (A compond statement is a block of code.)
Adding curly braces around case 2: statements will make the code compile and run as shown below.
Let's start with an example. Here is a sample string. It'd be nice to improve readability by splitting it into multiple lines.
const char *quote = "The shepherd drives the wolf from the sheep's for which the sheep thanks the shepherd as his liberator, while the wolf denounces him for the same act as the destroyer of liberty. Plainly, the sheep and the wolf are not agreed upon a definition of liberty.";
Couple of ideas.
Line continuation: split the string anywhere at white space, and end the line with a backslash (\). Repeat until done.
Backslash (\) is the continuation character often referred as backslash-newline.
eg.,
const char *quote = "The shepherd drives the wolf from the sheep's for which \
the sheep thanks the shepherd as his liberator, while \
the wolf denounces him for the same act as the destroyer \
of liberty. Plainly, the sheep and the wolf are not agreed \
upon a definition of liberty.";
The C preprocessor removes the backslash and joins the following line with the current one. This is repeated until all lines are joined. However in the above example, indentation becomes part of the actual string thus a bunch of unwanted whitespaces appear in the final string. Besides, it is not possible to include comments at the end of any of those lines [after the line continuation character] if you ever wanted to. Both of these minor issues can be avoided with string literal concatenation (discussed next).
Compiling the above with Solaris Studio C compiler results in the following output.
The shepherd drives the wolf from the sheep's for which the sheep thanks the shepherd as his liberator, while the wolf denounces him for the same act as the destroyer of liberty. Plainly, the sheep and the wolf are not agreed upon a definition of liberty.
String literal concatenation: split the string anywhere at white space, and end the line with a pair of quotes ("). Start the next line with quotes, and repeat until done.
eg.,
const char *quote = "The shepherd drives the wolf from the sheep's for which " /* dummy comment */
"the sheep thanks the shepherd as his liberator, while " // another dummy comment
"the wolf denounces him for the same act as the destroyer "
"of liberty. Plainly, the sheep and the wolf are not agreed "
"upon a definition of liberty.";
Adjacent string literals are concatenated at compile time. Comments outside the string literals are ignored, and the concatenated string will not include indented whitespaces unless they are within the string literals (delimited by quotes).
Printing the above results in the following output.
The shepherd drives the wolf from the sheep's for which the sheep thanks the shepherd as his liberator, while the wolf denounces him for the same act as the destroyer of liberty. Plainly, the sheep and the wolf are not agreed upon a definition of liberty.
2. Simultaneous writing to multiple streams
The straight forward approach is to make multiple standard I/O library function calls to write to desired streams.
This approach may work well as long as there are only a few occurrences of such writing. However if there is a need to repeat it many times over the lifetime of a process, it can be simplified by wrapping all those function calls that write to different streams into a function so a single call to the wrapper function takes care of writing to different streams. If the number of arguments in the formatted string is not constant or not known in advance, one option is to make the wrapper function a variadic function so that it accepts a variable number of arguments. Here is an example.
The following example writes all messages to the log file, writes only informative messages to standard output (stdout) and writes only fatal errors to the high priority log. Without the logfmtstring() wrapper function, the same code would have had five different standard I/O library calls rather than just three that the sample code has.
% cat multstreams.c
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
FILE *log, *highprioritylog;
void logfmtstring(const char *fmtstr, ...) {
va_list args;
va_start(args, fmtstr);
vfprintf(log, fmtstr, args);
if (strstr(fmtstr, "[info]") != NULL) {
vfprintf(stdout, fmtstr, args);
}
if (strstr(fmtstr, "[fatal]") != NULL) {
vfprintf(highprioritylog, fmtstr, args);
}
va_end(args);
}
void some_function(..) {
log = fopen("app.log", "w");
highprioritylog = fopen("app_highpriority.log", "w");
...
logfmtstring("[info] successful entries: %d. failed entries: %d\n", completecount, failedcount);
logfmtstring("[fatal] billing system not available\n");
logfmtstring("[error] unable to ping internal system at %s\n", ip);
...
fclose(log);
fclose(highprioritylog);
}
% cc -o mstreams multstreams.c
% ./mstreams
[info] successful entries: 52. failed entries: 7
% cat app.log
[info] successful entries: 52. failed entries: 7
[fatal] billing system not available
[error] unable to ping internal system at 10.135.42.36
% cat app_highpriority.log
[fatal] billing system not available
%
Web search keywords: C Variadic Functions
3. Declaring variables within the scope of a CASE label in a SWITCH block
It is possible to declare and use variables within the scope of a case label with one exception -- the first statement after a case label should be a statement or an expression, but not a declaration. If not compiler throws an error during compilation such as the following.
% cat switch.c
#include <stdio.h>
int main() {
switch(NULL) {
default:
int cyear = 2015;
printf("\nin default ..");
printf("\nyear = %d", cyear);
}
}
% cc -V -o switch switch.c
cc: Sun C 5.12 SunOS_sparc Patch 148917-08 2014/09/10
acomp: Sun C 5.12 SunOS_sparc Patch 148917-08 2014/09/10
"switch.c", line 6: syntax error before or at: int
"switch.c", line 8: undefined symbol: cyear
cc: acomp failed for switch.c
The above failure can be fixed by either moving the variable declaration to any place after a valid statement if possible, or by adding a dummy or null statement right after the case label.
eg.,
1. Move the declaration from right after the case label to any place after a valid statement.
/* works */
switch(NULL) {
default:
printf("\nin default ..");
int cyear = 2015;
printf("\nyear = %d", cyear);
}
2. Add a dummy or null statement right after the case label.
Relying on *stat() API is not much of an option when using wildcards to match a filename pattern. Some of the options involve traversing a directory checking each file for a match using fnmatch(), or to use system() function to execute an equivalent shell command. Another option that is well suited for this task is the glob*() API, which is part of Standard C Library Functions. (I believe glob() depends on fnmatch() for finding matches).
Here is a simple example that displays the number of matches found for pattern "/tmp/lint_" along with the listing of matches.
% ls -1 /tmp/lint_*
/tmp/lint_AAA.21549.0vaOfQ
/tmp/lint_BAA.21549.1vaOfQ
/tmp/lint_CAA.21549.2vaOfQ
/tmp/lint_DAA.21549.3vaOfQ
/tmp/lint_EAA.21549.4vaOfQ
/tmp/lint_FAA.21549.5vaOfQ
/tmp/lint_GAA.21549.6vaOfQ
% cat match.c
#include <stdio.h>
#include <glob.h>
int main(int argc, char **argv) {
glob_t buf;
if (argc == 1) return 0;
glob(argv[1], 0 , NULL , &buf);
printf("\nNumber of matches found for pattern '%s': %d\n",
argv[1], buf.gl_pathc);
for (int i = 0; i < buf.gl_pathc; ++i) {
printf("\n\t%d. %s", (i + 1), buf.gl_pathv[i]);
}
globfree(&buf);
return 0;
}
% cc -o match match.c
% ./match /tmp/lint_\*
Number of matches found for pattern '/tmp/lint_*': 7
1. /tmp/lint_AAA.21549.0vaOfQ
2. /tmp/lint_BAA.21549.1vaOfQ
3. /tmp/lint_CAA.21549.2vaOfQ
4. /tmp/lint_DAA.21549.3vaOfQ
5. /tmp/lint_EAA.21549.4vaOfQ
6. /tmp/lint_FAA.21549.5vaOfQ
7. /tmp/lint_GAA.21549.6vaOfQ
Please check the man page out for details -- glob(3C).
2. Microtime[stamp]
One of the old blog posts has an example to extract the current timestamp using time API. It shows the timestamp in standard format month-date-year hour:min:sec. In this post, let's add microseconds to the timestamp.
One major change from old approach is the reliance on gettimeofday() since it returns a structure [timeval] with a member variable [tv_usec] to represent the microseconds.
strftime() fills up the date/time data in timestamp variable as per the specifiers used in time format (third argument). By the time strftime() completes execution, timestamp will have month-date-year hr:min:sec filled out. Subsequent snprintf fills up the only remaining piece of time data - microseconds - using the tv_usec member in timeval structure and writes the updated timestamp to a new variable, etimestamp.
I have my doubts about this header - so, let me show an example first. The following rudimentary example attempts to construct a sentence that is something like "value of pi = (22/7) = 3.14". In other words, the sentence has a mixture of character strings, integers, floating point number and special characters.
% cat fmt.c
#include <stdio.h>
#include <string.h>
int main() {
char tstr[48];
char pistr[] = "value of pi = ";
int num = 22, den = 7;
float pi = ((float)num/den);
char snum[8], sden[8], spi[8];
sprintf(sden, "%d", den);
sprintf(snum, "%d", num);
sprintf(spi, "%0.2f", pi);
strcpy(tstr, pistr);
strcat(tstr, "(");
strcat(tstr, snum);
strcat(tstr, "/");
strcat(tstr, sden);
strcat(tstr, ") = ");
strcat(tstr, spi);
puts(tstr);
return 0;
}
% cc -o fmt fmt.c
% ./fmt
value of pi = (22/7) = 3.14
Nothing seriously wrong with the above code. It is just that it uses a bunch of sprintf(), strcpy() and strcat() calls to construct the target string. Also it overallocates the memory required for the actual string.
The same effect can be achieved by using asprintf(). The resulting code will be much smaller and easy to maintain however. This function also eases the developer from the burden of allocating memory of appropriate size. In general, overallocation leads to memory wastage and underallocation likely leads to buffer overflows posing unnecessary security risks. When relying on asprintf(), developers are not relieved from two factors though -- checking the return value to see if the call succeeded, and in freeing up the buffer when done with it. Ignoring those two aspects lead to program failures in the worst case, and memory leaks are almost guaranteed.
Here is the alternate version that achieves the desired effect by making use of asprintf().
% cat ifmt.c
#include <stdio.h>
#include <stdlib.h>
int main() {
char *tstr;
int num = 22, den = 7;
float pi = ((float)num/den);
int ret = asprintf(&tstr, "value of pi = (%d/%d) = %0.2f", num, den, pi);
if (ret == -1) return 1;
puts(tstr);
free(tstr);
return 0;
}
% cc -o ifmt ifmt.c
% ./ifmt
value of pi = (22/7) = 3.14
ceil() rounds the argument upward to the nearest integer value in floating-point format. For example, calling ceil() with an argument (2/3) should return 1.
The reason for the incorrect result in the first attempt can be attributed to the integer division. Since both operands in the division operation are integers, it resulted in an integer division which discarded the fractional part.
Desired result can be achieved by casting one of the operands to float or double as shown in the subsequent attempt.
(2) Main difference between abort() and exit() calls
On a very high level: abort() sends SIGABRT signal causing abnormal termination of the target process without calling functions registered with atexit() handler, and results in a core dump. Some cleanup activity may happen.
exit() causes normal process termination after executing functions registered with the atexit() handler, and after performing cleanup activity such as flushing and
closing all open streams.
If it is desirable to bypass atexit() registered routine(s) during a process termination, one way is to call _exit() rather than exit().
Of course, this is all high level and the information provided here is incomplete. Please check relevant man pages for detailed information.
(3) Current timestamp
The following sample code shows the current timestamp in two different formats. Check relevant man pages for more information.
#include <time.h>
..
char timestamp[80];
time_t now;
struct tm *curtime;
now = time(NULL);
curtime = localtime(&now);
strftime(timestamp, sizeof(timestamp), "%m-%d-%Y %X", curtime);
printf("\ncurrent time: %s", timestamp);
printf("\ncurrent time in a different format: %s", asctime(curtime));
..
Executing this code shows output
current time: 07-31-2014 22:05:42
current time in a different format: Thu Jul 31 22:05:42 2014
.. with little commentary aside. Target audience: new programmers. These tips are equally applicable in C and C++ programming environments.
1. Duplicating a file pointer
Steps: find the integer file descriptor associated with the file stream using fileno() call, make a copy of the file descriptor using dup() call, and finally associate the file stream with the duplicated file descriptor by calling fdopen().
2. Capturing the exit code of a command that was invoked using popen()
Using pipes is one way of executing commands programmatically that are otherwise invoked from a shell. While pipes are useful in performing tasks other than executing shell commands, this tip is mainly about the exit code of a command (to figure out whether it succeeded or failed) that was executed using popen() API.
To capture the exit code, simply use the value returned by pclose(). This function call returns the termination status of the command that was executed as a child process. However the termination status of the child process is in the top 16 bits of the return value, so dividing the pclose() return value by 256 gives the actual exit code of the command that was executed.
Standard C library has implementation for converting a string to an integer (atoi()), but not for converting an integer to a string. One way to achieve the desired result is by using sprintf() function call, which writes formatted data to a string.