I've written a simple C program to convert char into Tokens. Things work fine but I'm unable to understand why the size variable value is changing.
typedef struct _token {
int val;
} Token;
void parse( char* code, int size, Token** tokens ) {
int i = 0;
for (; i < size; i++) {
tokens[i] = malloc(sizeof(Token));
tokens[i]->val = code[i];
}
}
int execute( char *path ) {
char* code;
if ( read_file( path, &code ) != 0 ) {
return -1;
}
int size = strlen(code) - 1;
printf("BEFORE PARSE: %d\n", size); // 1st printf
Token *tokens;
parse( code, size, &tokens );
printf("AFTER PARSE: %d\n", size); // 2nd printf
return 0;
}
if code contains "abcde", the output is:
BEFORE PARSE: 5
AFTER PARSE: 142786584
The second printf displays different values on different runs.
Please help !
PS: I'm a C noob !
EDIT:
int read_file(char* path, char** code) {
FILE* fp = fopen ( path , "rb" );
if( !fp ) {
return -1;
}
fseek( fp , 0L , SEEK_END);
long lSize = ftell( fp );
rewind( fp );
/* allocate memory for entire content */
*code = calloc( 1, lSize+1 );
if( !*code ) {
fclose( fp );
return -1;
}
/* copy the file into the buffer */
if( 1 != fread( *code , lSize, 1 , fp) ) {
fclose(fp);
return -1;
}
fclose( fp );
return 0;
}
You have a typical case of buffer overflow.
char* code;
Allocates a pointer to character (typically 8 bytes), not a buffer to hold your file data.
Same with
Token *tokens;
When you write to tokens in parse you overwrite part of your stack and size with it.
Allocate enough memory for them!
char * code = malloc(0x1000);
Token *tokens = malloc(0x100 * sizeof(Token *));
And pass the pointer, not it's address:
read_file( path, code );
parse( code, size, tokens );
Here is corrected code:
typedef struct _token {
int val;
} Token;
void parse( char* code, int size, Token* tokens ) {
int i = 0;
for (; i < size; i++) {
// you already have memory now
tokens[i]->val = code[i];
}
}
int execute( char *path ) {
char* code = malloc(0x1000);
if ( read_file( path, code ) != 0 ) {
return -1;
}
int size = strlen(code) - 1;
printf("BEFORE PARSE: %d\n", size); // 1st printf
Token *tokens = calloc(sizeof(Token), 0x100);
parse( code, size, tokens );
printf("AFTER PARSE: %d\n", size); // 2nd printf
return 0;
}
It is because tokens is never initialized. Change it to:
Tokens **tokens = malloc(sizeof(Tokens *) * size);
Don't forget to free the memory when you are done with it:
for (; i < size; i++) {
free(tokens[i]);
}
free(tokens);
Related
i'm trying to fill each row of the array with each word of the file.
I don't want to overallocate memory , so i want to know atleast the lenght of the longest word and the number of rows i should allocate, so the number of words written in the file.
I can't understand where is the problem in the code. I think it should be a problem with counting the longest word since when i print longest_file_word after assigning the value returned by the function it prints -1.
Obviously it doesnt work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int longestWord(char *file, int *nWords);
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen);
int main(int argc, char *argv[]){
int file_elements_number=0 , i , j , k , z , longest_file_word , count_file_words ;
char *filename =(char*)malloc((strlen(argv[2]) +1 )*sizeof(filename));
strcpy( filename , argv[1]);
for(i = 0; i < strlen(argv[1])+1 ; i++){
printf("%c" , filename[i]);
}
if(filename = NULL){
printf("Non c'e' abbastanza memoria");
return 1;
}
if(argc!=2)
{
printf("Errore numero parametri passati da linea di comando\n");
return 1;
}
longest = longestWord( filename , &count);
printf("ciao %d\n%d\n", count , longest);
char **file_words = Create2DStr(count, longest);
FILE *file_ptr;
const char delim[] = {" \n\t"};
char line[260];
char *buf = NULL;
file_ptr = fopen( filename, "r");
count=0;
while(fgets(line, 260, file_ptr))
{
buf = strtok(line, delim);
while(buf)
{
if((strlen(buf) > 0)){
strcpy(file_words[count], buf);
count++;
}
buf = strtok(NULL, delim);
}
}
for(i = 0 ; i < count ; i++){
for( j = 0 ; j < longest ; j++){
printf("%c" , file_words[i][j]);
}
printf("\n");
}
fclose(file_ptr);
free(filename);
filename = NULL;
return 0;
}
int longestWord(char *filename, int *nWords)
{
FILE *file_ptr=0;
int cnt=0, longest=0, numWords=0;
char c;
file_ptr = fopen(filename, "r");
if(file_ptr){
while ( (c = fgetc(file_ptr) ) != EOF )
{
if ( isalnum (c) ) {
cnt++;
}
else if ( ( ispunct (c) ) || ( isspace(c) ) || (c == '\0' ) || (c== '\n'))
{
(cnt > longest) ? (longest = cnt, cnt=0) : (cnt=0);
numWords++;
}
}
*nWords = numWords;
fclose(file_ptr);
}
else {
return -1;
}
return longest;
}
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen){
int i;
char **a = {0};
a =(char**) calloc(numStrings, sizeof(a));
for(i=0;i<numStrings; i++)
{
a[i] = (char*)calloc(maxStrLen + 1, 1);
}
return a;
}
You're doing,
if(filename = NULL)
rather than,
if(filename == NULL)
after reading your filename.
You should be compiling with warnings turned on, -Wall on gcc.
The result -1 means that function longestWord cannot open the specified file name which may be a result of the if(filename = NULL)
Apart from this it is difficult to understand what you are doing with argv[1] and argv[2] to prepare filename. You allocate memory based on the string length of argv[2], then copy the string from argv[1] which could be longer.
You should do the checks of argc and filename before you access argv or filename.
I want to change my input.txt file to an integer array.
But sadly I keep missing one integer whenever new-line-character is met.
Following is my main()
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
val = convert(STRING);
return 0;
}
Following is my file input function
char *readFile() {
int count;
FILE *fp;
fp = fopen("input.txt", "r");
if(fp==NULL) printf("File is NULL!n");
char* STRING;
char oneLine[255];
STRING = (char*)malloc(255);
assert(STRING!=NULL);
while(1){
fgets(oneLine, 255, fp);
count += strlen(oneLine);
STRING = (char*)realloc(STRING, count+1);
strcat(STRING, oneLine);
if(feof(fp)) break;
}
fclose(fp);
return STRING;
}
Following is my integer array function
int *convert(char *STRING){
int *intarr;
intarr = (int*)malloc(sizeof(int)*16);
int a=0;
char *ptr = strtok(STRING, " ");
while (ptr != NULL){
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " ");
}
return intarr;
}
There are many issues.
This is a corrected version of your program, all comments are mine. Minimal error checking is done for brevity. intarr = malloc(sizeof(int) * 16); will be a problem if there are more than 16 numbers in the file, this should be handled somehow, for example by growing intarr with realloc, similar to what you're doing in readFile.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
char *readFile() {
FILE *fp;
fp = fopen("input.txt", "r");
if (fp == NULL)
{
printf("File is NULL!n");
return NULL; // abort if file could not be opened
}
#define MAXLINELENGTH 255 // define a constant rather than hardcoding "255" at several places
char* STRING;
char oneLine[MAXLINELENGTH];
STRING = malloc(MAXLINELENGTH);
int count = MAXLINELENGTH; // count mus be initialized and better declare it here
assert(STRING != NULL);
STRING[0] = 0; // memory pointed by STRING must be initialized
while (fgets(oneLine, MAXLINELENGTH, fp) != NULL) // correct usage of fgets
{
count += strlen(oneLine);
STRING = realloc(STRING, count + 1);
strcat(STRING, oneLine);
}
fclose(fp);
return STRING;
}
int *convert(char *STRING, int *nbofvalues) { // nbofvalues for returning the number of values
int *intarr;
intarr = malloc(sizeof(int) * 16);
int a = 0;
char *ptr = strtok(STRING, " \n"); // strings may be separated by '\n', or ' '
*nbofvalues = 0;
while (ptr != NULL) {
intarr[a] = atoi(ptr);
printf("number = %s\tindex = %d\n", ptr, a);
a++;
ptr = strtok(NULL, " \n"); // strings are separated by '\n' or ' '
} // read the fgets documentation which
// terminates read strings by \n
*nbofvalues = a; // return number of values
return intarr;
}
int main(int args, char* argv[]) {
int *val;
char *STRING = readFile();
if (STRING == NULL)
{
printf("readFile() problem\n"); // abort if file could not be read
return 1;
}
int nbvalues;
val = convert(STRING, &nbvalues); // nbvalues contains the number of values
// print numbers
for (int i = 0; i < nbvalues; i++)
{
printf("%d: %d\n", i, val[i]);
}
free(val); // free memory
free(STRING); // free memory
return 0;
}
I'm not sure what your requirement is, but this can be simplified a lot because there is no need to read the file into memory and then convert the strings into number. You could convert the numbers on the fly as you read them. And as already mentioned in a comment, calling realloc for each line is inefficient. There is room for more improvements.
Why do I get 0 bytes after a block of size 7 alloc'd error when I am still leaving space for '\0'?
I tried allocating and reallocating 7 bytes and kept the size variable going up by 5 so that there would always be at least 2 bytes left at the end when I add the null terminator, but I am still getting the valgrind error:
Invalid write of size 1:
0 bytes after a block of size 7 alloc'd
whenever I read or write to token, for example I get it on this line:
token[i] = read;
void parse_file(char file[]) {
char read = 0;
int size = 5;
char *token = NULL;
int i = 0;
FILE *fp = NULL;
token = malloc(7 * sizeof(char));
fp = fopen(file, "r");
if(fp == NULL) {
fprintf(stderr, "%s: No such file or directory\n", file);
free(token);
fclose(fp);
return;
}
read = fgetc(fp);
while(read != EOF) {
if(i == size) {
token = realloc(token, 7 * sizeof(char));
size += 5;
}
if(isalpha(read)) {
read = (char) tolower(read);
token[i] = read;
}
else {
if(isalpha(token[0])) {
token[i] = '\0';
put(token);
}
else {
free(token);
}
token = calloc(7,sizeof(char));
size = 5;
i = 0;
read = fgetc(fp);
continue;
}
read = fgetc(fp);
i++;
}
free(token);
fclose(fp);
}
the following proposed code:
cleanly compiles
eliminates unnecessary code/logic
performs the desired functionality
properly checks for errors
incorporates the comments to the OPs question
incorporates the comments to this answer
and now the proposed code: (EDITED)
#include <stdlib.h> // exit(), EXIT_FAILURE, realloc(), free()
#include <stdio.h> // FILE, fprintf(), fopen(), fgetc(), perror()
#include <ctype.h> // isalpha(), tolower()
#include <errno.h> // errno
#include <string.h> // strerror()
// prototypes
void parse_file(char fileName[]);
void parse_file(char fileName[])
{
int byteRead = 0;
size_t size = 0;
char *token = NULL;
size_t i = 0;
FILE *fp = NULL;
fp = fopen(fileName, "r");
if( !fp )
{
fprintf(stderr, "Can't open %s: %s\n", fileName, strerror(errno));
exit( EXIT_FAILURE );
}
while( (byteRead = fgetc(fp) ) != EOF )
{
char *temp = NULL;
if(i >= size)
{
temp = realloc(token, 7 + size );
if( !temp )
{
perror( "realloc failed" );
free( token );
fclose( fp );
exit( EXIT_FAILURE );
}
// implied else, realloc successful
size += 7;
token = temp;
}
if( isalpha(byteRead) )
{
byteRead = tolower(byteRead);
token[i] = (char)byteRead;
i++;
}
else if( i )
{
token[i] = '\0';
puts(token);
free( token );
i = 0;
size = 0;
}
}
free(token);
fclose(fp);
}
I want to sort strings from file; this code compiles well, but it stops working in line 29, when I do words_array[i] = strdup(line);.
From debugger I have "program received signal sigsegv segmentation fault"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int comparator ( const void * elem1, const void * elem2 )
{
return strcmp( *(const char**) elem1, *(const char**) elem2);
}
int main()
{
char filename[]="dane.txt";
FILE* fp;
char* line = NULL;
size_t len = 0;
char** words_array = NULL;
int i = 0,j; // number of elements
// read list from file
if( ( fp = fopen(filename, "r") ) == NULL ) {
fprintf(stderr, "Cannot open source file %s!\n", filename);
exit(1);
}
for(; fgets(line, len, fp) != NULL; ++i) {
// put word in array
words_array = realloc(words_array, sizeof(char*) * (i + 1) );
words_array[i] = strdup(line);
}
fclose(fp);
free(line);
// sort it
qsort(words_array, i, sizeof(char*), comparator);
if( ( fp = fopen(filename, "a+") ) == NULL ) {
fprintf(stderr, "Cannot open source file %s!\n", filename);
exit(1);
}
// write to file and free dynamically allocated memory
for(j = 0; j < i; ++j) {
fprintf(fp, "%s", words_array[j]);
free(words_array[j]);
}
free(words_array);
fclose(fp);
return 0;
}
You never allocated space for line to point to.
Ok, so I have to write a C program that allocated memory dynamically, that reads n lines of char text and that counts number of appearances of a specific word. Unfortunately, after I read n, then the n lines, then the m and k, it won't read cuv1 and it will always show 0. Any idea why?
This is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char c[100][100];
int n;
int prima(const char *s1, char *cuv1)
{
int ok=0, i;
char *p;
p=strstr(s1, cuv1);
while(p)
{
ok++;
strcpy(p, p+strlen(cuv1));
p=strstr(p, cuv1);
}
return ok;
}
int main()
{
int m, k, i, l, nr=0;
char t[20], s1[12000];
scanf("%d", &n);
char *text, *v[n], cuv1[12000];
getchar();
for(i=0;i<n;i++)
{
text=malloc(12000*sizeof(char));
fgets(text, 12000, stdin);
l=strlen(text);
text[l-1]='\0';
l=l-1;
v[i]=malloc(l*sizeof(char));
strcpy(v[i], text);
}
scanf("%d", &m);
for(i=1;i<=m;i++)
{
scanf("%d", &k);
if(k==1)
{
fgets(cuv1, 12000, stdin);
for(i=1;i<=n;i++)
{
strcpy(s1, v[i]);
nr=nr+prima(s1);
}
printf("%d", nr);
}
}
return 0;
}
Unfortunately I get this error: incompatible pointer to integer conversion passing 'char [12000]' to parameter of type 'char'
Check the prototype of strstr - then you'll see your p=strstr(s1, cuv1); is wrong as cuv1 is a char, not a const string for the second parameter of strstr (const char *).
char * strstr ( const char *, const char * );
So for a start, change your int prima(char s1) to int prima(const char *s1), or int prima(const char s1[]).
after applying the comments and fixing some logic problems
the following code makes significant use of malloc() and free()
You will need to add any multiple search string capabilities
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int prima( char *strToSearch, char *strToFind)
{
int countFound=0;
char *p = strToSearch;
do
{
if( NULL != (p = strstr( p, strToFind) ) )
{ // then sub string found
countFound++;
p += strlen(strToFind);
}
} while(p);
return countFound;
} // end function: prima
void flushstdin( void )
{
int ch;
while( (ch = getchar()) != EOF && '\n' != ch) ;
}
void cleanup( char **linesToFree, int lineCount )
{
for( int i=0; i < lineCount; i++ )
{
free( linesToFree[i] );
}
}
int main( void )
{
int countLinesToRead = 0;
printf( "Enter number of lines to read: " );
if( 1 != scanf("%d", &countLinesToRead) )
{ // then scanf failed
perror( "scanf for count of lines to read failed" );
exit( EXIT_FAILURE );
}
// implied else, scanf successful
char **ptrToLines = NULL;
if( NULL == (ptrToLines = malloc( countLinesToRead*sizeof(char*) ) ) )
{ // then malloc failed
perror( "malloc for array of char pointers failed" );
exit( EXIT_FAILURE );
}
// implied else, malloc successful
// init to all NULLs so easy to recover if error occurs
for( int i = 0; i < countLinesToRead; i++ )
{
ptrToLines[i] = NULL;
}
flushstdin();
for( int i = 0; i < countLinesToRead; i++ )
{
size_t lineLen = 1;
if( 0 < getline( &ptrToLines[i], &lineLen, stdin) )
{ // then read of line successful
// remove any trailing newline char
char *newline = NULL;
if( NULL != (newline = strstr( ptrToLines[i], "\n") ) )
{ // then newline to be trimmed
*newline = '\0';
}
}
else
{ // getline failed
perror( "getline for line to search failed" );
cleanup( ptrToLines, countLinesToRead );
exit( EXIT_FAILURE );
}
}
char *strToFind = NULL;
size_t searchLen = 1;
printf( "Enter sub string to search for: " );
if( 0 < getline( &strToFind, &searchLen, stdin) )
{ // then read of line successful
// remove any trailing newline char
char *newline = NULL;
if( NULL != (newline = strstr( strToFind, "\n") ) )
{ // then newline to be trimmed
*newline = '\0';
}
}
else
{ // getline failed
perror( "getline for string to find failed" );
cleanup( ptrToLines, countLinesToRead );
exit( EXIT_FAILURE );
}
int countFound = 0;
for(int i=0; i<countLinesToRead; i++)
{
countFound += prima( ptrToLines[i], strToFind);
}
printf("%d\n", countFound);
cleanup( ptrToLines, countLinesToRead );
return 0;
}
here is the results of a run of the above code:
Enter number of lines to read: 2
adfadfadfadf adf adb
abcdef adfadf
Enter sub string to search for: adf
7