Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Thursday, April 02, 2020

Writing my first C program in Linux

Recently I wrote my first C program in Ubuntu Linux for which I had a deadline. I was already familiar with C/C++ on Windows. It was quite a learning experience. My method was as follows:
  1. Search the internet for projects matching the problem definition. Result: There weren't any.
  2. Search examples/tutorials for terms in the problem definition like fork, poll.
  3. Write small demos. Modify and combine examples to solve the original problem. Result: Faced many difficulties during adaptation.
  4. Dive deeper and understand the details of concepts/terms that I am not well versed in, like pipes.
  5. Apply that deeper knowledge.
  6. Increase depth until the problem is solved in its entirety.
As you can see, I did not start by reading a lengthy Linux C fundamentals book. I focused on the problem at hand, tried shortcuts first and only spent more time on problematic points. I felt a lot of frustration while trying to hack my way out but I was determined enough to overcome constant failures. If you are working under time pressure, this is the fastest method of getting things done.

Notes:
  • To display processes starting with "node": ps -ef|grep node
  • To run as a background process, add & to the end.
  • Windows Subsystem for Linux, using Ubuntu with VS Code, tasks.json to build file (ctrl + shift+b)
  • pipes, socketpair, filedescriptor, dup2. 
  • Redirecting stdin
  • fork - exec
  • poll
  • Converting a string to a vector of strings, with space as delimiter
  • Difficulty of using strings. Writing and reading structures is much easier, to read a string after a write, you need to send EOF with close(fd), but then you have the problem of opening the filedescriptor again.
  • gcc, makefile, tar
  • Difficult to find bug in C: for (int i = 0; len; i++). Note that i< is missing.
  • When you print elements of a union structure, you will only get the last set element right.

Tuesday, October 01, 2019

Why you should take warning C4013 seriously

A piece of legacy C code that was working fine on Windows 7 64 bit was crashing on Windows 10 64 bit. After a couple of days of detective work I found out that it was related to warning C4013: 'malloc' undefined; assuming extern returning int. Simplified version of the code is as follows:
When I build the code with Visual Studio with the x64 platform selected, I get two warnings:

When I run it on a Windows 10 64 bit PC with x64 setting, I get access violation:

When I look at the variable m->a, I see "unable to read memory":

To solve the problem, I have to include stdlib.h:

Explanation: You get away with it on a 32 bit build because the size of int is the same as the size of a pointer. On the other hand if you build your program as 64 bit program, the warnings become really relevant, because now pointers are 64 bits wide, but as the compiler assumes that malloc etc. return ints (i.e. 32 bit instead of 64 bit for pointer) everything get messed up.

Why does the compiler issue a warning instead of an error, even tough it cannot find definition of malloc: The symbol foo, without a declaration, is totally unknown to the compiler. Because it just compiles your code, but it's not responsible for the linkage of your symbols (I say symbols because this can include variables and functions)... The linker sees the symbol foo(4 bytes) and will start to look for any corresponding definitions of foo(4 bytes). And if it finds that (say, in another module of yours, or in the libc of your system, or as syscall wrapper), then the linker is content and it will create the executable.

When I commented out the stdio.h include, I got C4013 warnings for both printf() and getchar() as expected, but I also got a linker error for printf: "LNK 2019 unresolved external symbol". When I looked inside stdio.h for their definitions, I saw that the definition of printf was more complex and contained __CRTDECL, defined in vcruntime.h which I think is part of visual studio specific libraries, not "core" C/C++. vcruntime.h resides under Program Files (x86)/Microsoft Visual Studio 14.0/VC/... folders, while stdio.h resides under Program Files (x86)/Windows Kits/10... folders. I guess the linker always looks into the standard libraries, even if you don't include their headers.

Why is it crashing on Windows7 64 bit but working fine on Windows 10 64 bit? I don't know :(

Bonus 1: You can configure Visual Studio to treat 4013 warning as error so that the build stops.

Bonus 2: All the different reasons that can lead to a LNK2019 unresolved external symbol error. Recently I got it because I tried to use a cpp function from a c file.

Music: Internal Conflict (Black Mesa: Xen Soundtrack) - Joel Nielsen

Wednesday, August 28, 2019

Problem with mixing C++ and C code

In a C++ project that also contained legacy C files, I started to get the error "C2664 ...cannot convert argument 3 from void * to const_locale_t". The argument in question was NULL. Changing the include order by including the legacy header after stdio.h solved the problem but I wanted to understand the root problem to avaiod unnecessary technical debt.

One of the C headers contained #define NULL ((void *)0) which is standard for C but not for C++. Normally NULL is defined inside vcruntime.h (part of Visual C++). Using the legacy NULL before any code that depends on vcruntime.h, like string.h, stdio.h, caused this error. Removing the custom definition or changing it to the following solved the problem, see also my answer on StackOverflow:
Another strange error happens if you have defined fmax globally in your own C code because it also exists in cmath. The strange error will be C2039 'fmax': is not a member of 'global namespace'. Removing or renaming your own custom definition will solve the problem.

Wednesday, November 14, 2018

Mixing C++ and C and calling a C/C++ dll from Java

In Visual Studio, it is possible to mix C++ and C code, C++ files should have cpp extension, C files should have C extension, the compiler takes care of the rest. When including a C header file inside a C++ file, wrap it inside extern "C { #include "somfile.h"}. Otherwise, you will get LNK2019 (the dreaded "unresolved external symbol" error).

When you want to create a dll from this mixed project to be used with Java, in your wrapper header file of cpp implementation, you have to wrap the header body inside #ifdef __cplusplus extern "C" { #endif ...  #ifdef __cplusplus } #endif. Otherwise you will be able load the dll in Java but when you call a dll method, you will get UnsatisfiedLinkError.

When naming your functions in C/C++ Java interface functions section, do not use underscores at the end of the function name like "Java_package_name_myFunction_rad_s()" because in Java interface functions, undescores are only used as package name separators. If you ignore this, you will get UnsatisfiedLinkError and wonder why.

Tuesday, October 09, 2012

Heap corruption in C

I want to copy a string (source) to another variable (destination) in C. I can use the functions strcpy_s or strcpy for that. When compiled in debug mode (I use Visual Studio 2010), if destination buffer is too small to house the source string, strcpy_s throws an exception and stops execution. When compiled in release mode, it again stops but no messages are displayed. strcpy, on the other hand remains silent even in debug mode and the code crashes at a later malloc / free call which makes it much harder to find the problem.

Friday, May 25, 2012

C Adventures

Strings in C is a problematic matter for novices like me. You usually see that your first guess is wrong and are puzzled until you discover the "C" way. The simple '=' operator does not work (except initialization) and you have to use the strcpy function. Below is an example where I assign the file's name to the variable fileName and then use it in fopen (source code here):


Friday, July 01, 2011

Heap > Stack

I wrote the following code which attempts to allocate memory for a two dimensional array of known dimensions:

Saturday, June 25, 2011

Limiting an angle to [0 360) or bit operations for dummies

Let's write a C function which takes as input an angle in degrees and if that angle is outside [0, 360), limits it to [0 360). Normally I would write it like this:

Tuesday, May 03, 2011

C: Two dimensional arrays and pointer indexing

Pointer indexing of two dimensional array:
my2DArray[iCol][iRow] =
*(mpPointer+iRow*nCol+iCol);
"The C Programming Language", 2nd edition, Brian W. Kernighan, Dennis M. Ritchie:
[p.29] When the name of an array is used as an argument, the value passed to the function is the location or address of the beginning of the array - there is no copying of array elements. By subscripting this value, the function can access and alter any argument of the array.

[p.78] The unary operator & gives the address of an object, so the statement "p = &c;" assigns the address of c to the variable p, and p is said to "point to" c... The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to.

[p.83] By definition, the value of a variable or expression of type array is the address of element zero of the array... a reference to a[i] can also be written as *(a+i)... In short, an array-and-index expression is equivalent to one written as a pointer and offset... There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable; constructions like a=pa and a++ are illegal.

[p.83] As formal parameters in a function definition, "char s[];" and "char *s;" are equivalent...

Thursday, April 07, 2011

Reading a text file using C

I wanted to read the following text file which has an extra line at the end (test.txt):
345
 <--empty line

I wrote the following C code:

FILE *fp;
char line[80], *myResult;
fp = fopen("test.txt", "rt");
while (!feof(fp))
{
         myResult = fgets(line, 80, fp);
         printf("line = %s", line);
         printf("myResult = %s", myResult);
         printf("feof(fp) = %d\n", feof(fp));
}

The output was:

line = 345
myResult = 345
feof(fp) = 0
line = 345
myResult = (null)feof(fp) = 16

I was surprised to see “line = 345” being written twice although there is only one value inside the file. My guess is that when fgets reads the last line, it directly returns NULL and while doing so it does not modify the value that was pointed to by “line”, so “line” stays at the same value from previous step.

I modified the while loop as follows:

while (fgets(line, 80, fp) != NULL)
{
         printf("line = %s", line);
}

New output is just one line as expected:

line = 345

Lesson learnt: Don’t use eof() in your while loop, use the return value of fgets() to check end of file.