Link to home
Start Free TrialLog in
Avatar of Rohit Bajaj
Rohit BajajFlag for India

asked on

C program working on Linux but not on windows

Hi,
Consider the following program.
program2.txt
It's a C program and working perfectly on linux machine. I generated a windows executable using Cygwin gcc compiler. But the executable fails to work.
Not sure why it's running on linux but not on windows.

This program takes a command line parameter (filename) as argument.
And takes input a string and append that string to the file and displays the content of the file.

Avatar of Kent Olsen
Kent Olsen
Flag of United States of America image

What behavior are you seeing on Windows?
Avatar of Rohit Bajaj

ASKER

It appears that you have a memory allocation issue.

Lines is declared as (char**), meaning it's a pointer to a pointer.  That's fine.

But memory for UserString is never allocated, nor is any memory for any strings associated with Lines.


but the same program when I compile with gcc on linux. It works
So?  You're writing to random memory addresses.  In your linux environment the address(es) happen to work.  In windows, they do not.

Execute the loop enough times and the linux program will fail, too.


Doesn't the %m in scanf does automatically that for you. Allocation of memory etc
char *fileName = argv[1];    
FILE *fp = fopen(fileName, "a");    
char *userString;    
printf("Enter the string to append to file : ");    
scanf("%m[^\n]s", &userString);

Open in new window


Note just those lines.  userString is declared as a pointer.  In the scanf statement, the address of the userString variable is passed as the address to where the string should be written.  Since the userString variable is in the program stack, scanf is writing to a spot in the stack.  The contents of userString (the address of the buffer) should be passed to scanf.  That memory has never been allocated.
%m is not part of the C standard.  It's up to the individual implementations of the C compiler to include it (or not).  glibc recognizes %m, apparently the windows compiler that you're using does not, so you'll need to do your own memory allocation.

This comes at a price though, as you'll need to allocate an initial buffer larger than your largest expected input or you run the risk of overrunning the buffer.


It's been eons for me to program C..

I'm not sure what value sizeof(char **) returns on different platforms.
Yeah I got it. It's probably that only. So is there a way to fix this program.
Or some way I use a compiler on windows other than the cygwin gcc to fix this ?
Or may be some options passed to it
ASKER CERTIFIED SOLUTION
Avatar of Kent Olsen
Kent Olsen
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So it appears to me that man pages - https://man7.org/linux/man-pages/man3/scanf.3.html
are not a reliable way to code C programs if you want to confirm to C standards.
I always use to refer to them while coding a C program.
What to refer to ? Is there any good C standards website which details all functions properly ?
As can be seen above the linux man pages mentions about %m
What to refer to ? Is there any good C standards website which details all functions properly ?

How about going old school with a good book

https://www.google.com/books/edition/Practical_C_Programming/RzmsANQ4gaAC?hl=en&gbpv=0
I found even the getline function gives problem on certain compilers.
The program that I had I compiled it with :
gcc.exe (MinGW.org GCC Build-2) 9.2.0
It compiled fine.

And then I tried it on another system it failed with error -

User generated image

I checked the gcc version on this system it was - (output of gcc --version)
gcc (i686-posix-dwarf-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


INFO 43921 - Malicious Code

Interesting choice for a directory name
If you're having issues compiling between compilers the issue is generally with the code as stated above
That's the name of the course :-)
The what do I use scanf %m doesn't work, getline also.
I want to be able to read input string of variable length. Is there some good way to do it. Rather than landing into all these kind of troubles ?


getline() should be defined in stdio.h.  Does your source #include it?

Yes. This is the exact code :
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char *fileName = argv[1];
    FILE *fp = fopen(fileName, "a");
    char *userString = NULL;
    printf("Enter the string to append to file : ");
    int n;
    size_t cap = 0;
    getline(&userString, &cap, stdin);
    printf("%s", userString);

    fputs(userString, fp);
    fclose(fp);

    fp = fopen(fileName, "r");
    char *line = NULL;
    int lineNumber = 1;
    char **lines = malloc(sizeof(char **));
    int rv;
    while (getline(&line, &cap, fp) != EOF)
    {
        lines[lineNumber - 1] = line;
        lines = realloc(lines, (lineNumber + 1) * sizeof(char **));
        lineNumber++;
        line = NULL;
    }

    printf("\n\nContents of the file is : \n\n");
    for (int i = 0; i < lineNumber - 1; i++)
        printf("%s", lines[i]);

    for (int i = 0; i < lineNumber - 1; i++)
        free(lines[i]);

    free(lines);
    return 0;
}

Open in new window


It does the same thing that I posted earlier. I just removed %m and used getline. And it worked on one compiler and failed on another  (Windows gcc)
No memory has been assigned to userString before writing to it at line 12.

No memory has been assigned to line before writing to it at line 23.

I'm surprised that you didn't generate an exception in both environments.

SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Noci,

I haven't done any coding in linux environments in a while now.  Didn't know that to be the normal behavior these days.

But it would explain why his program works in linux but not windows.

Avatar of noci
noci

@Kent: Here is the function description...
GETLINE(3)                                                                                           Linux Programmer's Manual                                                                                          GETLINE(3)



NAME
       getline, getdelim - delimited string input

SYNOPSIS
       #include <stdio.h>
       ssize_t getline(char **lineptr, size_t *n, FILE *stream);
       ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
       getline(), getdelim():
           Since glibc 2.10:
               _POSIX_C_SOURCE >= 200809L
           Before glibc 2.10:
               _GNU_SOURCE

DESCRIPTION
       getline() reads an entire line from stream, storing the address of the buffer containing the text into *lineptr.  The buffer is null-terminated and includes the newline character, if one was found.
       If *lineptr is set to NULL and *n is set 0 before the call, then getline() will allocate a buffer for storing the line.  This buffer should be freed by the user program even if getline() failed.
       Alternatively, before calling getline(), *lineptr can contain a pointer to a malloc(3)-allocated buffer *n bytes in size.  If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updat‐
       ing *lineptr and *n as necessary.

       In either case, on a successful call, *lineptr and *n will be updated to reflect the buffer address and allocated size respectively.
       getdelim() works like getline(), except that a line delimiter other than newline can be specified as the delimiter argument.  As with getline(), a delimiter character is not added if one was not present in the input be‐
       fore end of file was reached.

RETURN VALUE
       On  success, getline() and getdelim() return the number of characters read, including the delimiter character, but not including the terminating null byte ('\0').  This value can be used to handle embedded null bytes in
       the line read.
       Both functions return -1 on failure to read a line (including end-of-file condition).  In the event of an error, errno is set to indicate the cause.

ERRORS

       EINVAL Bad arguments (n or lineptr is NULL, or stream is not valid).



       ENOMEM Allocation or reallocation of the line buffer failed.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).
       ┌──────────────────────┬───────────────┬─────────┐
       │Interface             │ Attribute     │ Value   │
       ├──────────────────────┼───────────────┼─────────┤
       │getline(), getdelim() │ Thread safety │ MT-Safe │
       └──────────────────────┴───────────────┴─────────┘

CONFORMING TO
       Both getline() and getdelim() were originally GNU extensions.  They were standardized in POSIX.1-2008.

SEE ALSO
       read(2), fgets(3), fopen(3), fread(3), scanf(3)

Open in new window

Regarding man pages: you need to invoke the man command on the system where you are writing code. man 3 scanf on my Linux system documents %m, man 3 scanf on my Cygwin system does not.
I have no windows system so i can;'t verify that or advise on what is visible/doable there. About what docs to consult, It was mentioned before . Including a description of how to write portable programs.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
echo most | wc