Size of an array in C is smaller than expected - arrays

I have a string in C named buff. It is defined as char *buf = malloc(size);. I'm new to C but think this means that the values of the bytes of data allocated is stored in buf. All code is below
When I run sizeof(buf) it gives a size of 8 despite it clearly being full of more than 8 characters. I also have problems with taking the returned pointer and putting it through strlen which gives a value double what I expect.
This is a function to create an array of characaters read from a file.
The comments below used an example file of input.txt with contents ()()(()()(
const char* readFile()
{
FILE* fp;
int c;
int nch = 0;
int size = 100;
fp = fopen("./input.txt","r");
//allocates a block of 100 bytes and sets buf as the values associated with those values
char *buf = malloc(size);
if(buf == NULL)
{
fprintf(stderr, "out of memory\n");
return NULL;
}
while((c = fgetc(fp)) != EOF) //sets c to the next character of fp and checks it isn't the end
{
if(nch >= size-1) //size-1 as need an extra character for '\0'
{
/* time to make it bigger */
//printf("size: %d \n",size);
size += 50;
buf = realloc(buf, size);
//size_t n = arraySize(buf);
//printf("buf is %zu chars long \n",n);
if(buf == NULL)
{
fprintf(stderr, "out of memory\n");
return NULL;
}
}
buf[nch++] = c; //steps nch and stores c
}
buf[nch++] = '\0';
printf("size: %d\n",size); //output 100
printf("nch: %d\n",nch); //output 11
buf = realloc(buf, nch); //sets the buf to the size it actually needs
printf("buf is %d long\n",sizeof(buf)); //outputs sizeof(buf) as 8.
printf("buf[0] is %d long\n",sizeof(buf[0])); //outputs sizeof(buf[0]) as 1.
printf("\"%s\"\n", buf); //shows it reads the file properly. outputs "0123456789"
return buf;
}
int main(){
const char *fileContents = readFile(); //defines it as the address of the first character
printf("\"%s\"\n", fileContents); //outputs the file contents correctly
int count = 0;
printf("strlen: %d\n",strlen(fileContents)); //outputs 10
int i, lenStr = strlen(fileContents);
for(i = 0; i < lenStr; i++){
char c = *(fileContents+i); //sets character at position i to c
printf("character: %c i: %d\n",c,i); //outputs expected value
switch(c){ //this works sort of and correctly identifies values
case '(':
printf("increasing count\n");
count++;
break;
case ')':
printf("decreasing count\n");
count--;
break;
default:
printf("things are going wrong \n");
break;
}
fileContents++;
}
printf("go up %d floors",count);
getchar();
return 0;
}
output of for loop with select statment
character: ( i: 0
increasing count
character: ( i: 1
increasing count
character: ( i: 2
increasing count
character: ) i: 3
decreasing count
character: ) i: 4
decreasing count
character: i: 5
things are going wrong
character: i: 6
things are going wrong
character: i: 7
things are going wrong
character: e i: 8
things are going wrong
character: s i: 9
things are going wrong
For some reason the output is in the wrong order before it starts breaking.
It feels like the problem is I don't fully understand pointers. I'm just not sure how I am misunderstanding them.

The main problem is that you are doing fileContents++;. This adds one to the pointer fileContents, sliding the place to view.
You should remove this so that the characters will be processed one-by-one thanks to i++ in the for loop.
Another problem is that you are invoking undefined behavior by passing data having wrong type to printf. sizeof operator and strlen returns size_t, but %d expects int. The correct format specifier to print size_t is %zu.

Related

How to resolve this problem relevant File and Null in C?

So I need output size of array in this file text and to do this I must break the loop in the last position by using NULL to break but the problem here that when arr[i] come to value 0, it equal to NULL and break at that position so my size of array is not complete. How to resolve it? Thanks for support!
The file .txt input:
3
4
0
5
6
The code:
#include <stdio.h>
int main() {
char a[20];
char e[40];
int arr[30];
int num, key, k = 0, len = 0;
printf("Enter a filename: ");
scanf("%s", &a);
scanf("%c", &e);
FILE* rfile;
rfile = fopen(a, "r");
if (rfile == NULL) {
printf("Not found the file !!!");
}
else {
printf("Successfully accessed the file: %s\n", a);
int i;
for (i = 0; i < 30; i++) {
fscanf(rfile, "%d", &arr[i]);
fscanf(rfile, "%c", &e);
if (arr[i] == NULL) { // PROBLEM HERE
break;
}
len++;
}
}
printf("The size of array: %d", len);
return 0;
}
You can find some more details regarding what NULL is here, but you should save NULL for pointer comparisons, not comparing against ints as you are doing. In fact, your usage generates a warning:
warning: comparison between pointer and integer
Despite that, 0 == NULL will evaluate to true. Since 0 is in your list of values, you prematurely break revealing your problem. Instead, you simply need to read the entire file, either until you run out of room in your array (already covered by your for loop) or reach the end of the file (designated by EOF). To determine that, you need to check the return value of fscanf. Below is an example of a possible implementation:
#include <stdio.h>
#include <stdlib.h>
int main() {
int arr[30];
int len = 0;
FILE* rfile;
rfile = fopen("file.txt", "r");
if (rfile == NULL) {
printf("Not found the file !!!");
exit(-1);
}
else {
int i;
for (i = 0; i < 30; i++) {
// fscanf returns the number of correctly matched items, or
// EOF when the end of the file is reached (or EOF on error)
int ret = fscanf(rfile, "%d", &arr[i]);
// did we get a correct match?
if (ret == 1)
{
// we matched one number as expected, increment len
len++;
}
// did we reach the end of file?
else if (ret == EOF)
{
// EOF can also indicate an error, check errno here to determine if
// an error occurred instead of end of file, if you want
break;
}
}
}
// prints 5 with your input file example
printf("The size of array: %d\n", len);
return 0;
}
I have no idea what you were trying to accomplish with e, so I removed that as well as other unused variables, and hardcoded user input.
arr is the array of ints arr[i] has type int. NULL is a pointer.
If 0 indicated the end of the data (sentinel value) then:
if (arr[i] == 0) break;
or in a short form
if (!arr[i]) break;

The goal is to read two strings into 2 different variables. Then we count the number of "C" in each string and output the result

Here is what I did:
int main ()
{
int count = 0;
int i, j;
char firstLine[10000000];
char secondLine[10000000];
scanf("%s", firstLine);
scanf("%s", secondLine);
for (i=0;i<10000000;i++) {
if (firstLine[i] == 'C') {count++;}
}
for (j=0;j<10000000;j++) {
if (secondLine[j] == 'C') {count++;}
}
printf("%d", count);
return 0;
}
I think it makes sense but there is some sort of segmentation error according to my compiler. What am I missing?
For example, an input of AB
BBAAB should yield an output of 0.
Something like this should do the trick:
#define BUF_MAX 100
int main ()
{
int count1 = 0, count2 = 0;
char firstLine[BUF_MAX];
char secondLine[BUF_MAX];
fgets(firstLine,BUF_MAX,stdin);
for (int i = 0; firstLine[i] != 0; ++i)
if (firstLine[i] == 'C') ++count1;
fgets(secondLine,BUF_MAX,stdin);
for (int i = 0; secondLine[i] != 0; ++i)
if (secondLine[i] == 'C') ++count2;
printf("String 1 has %d C's\n", count1);
printf("String 2 has %d C's\n", count2);
return 0;
}
The BUF_MAX sets the maximum size of each line buffer, which I chose to be 100 (99 characters + the terminating null). If this isn't enough, make it bigger... say 500. But 10,000,000 is absurd... that could be the complete text of all of the Harry Potter books combined, or perhaps the complete works of Shakespeare!
The for loops continue as long as the character in the string is non-zero, i.e. until the terminating null character is encountered.
You should stop counting (break;) when you find a 0-terminator in your lines.
At the moment, you are going through an uninitialized garbage looking for random C

C - Array returns different values from the same element when put into variable

I'm very new to programming.
My problem in short:
arr[0]=1 arr[1]=2 arr[2]= -4
printf("\n Element 0 when in array: %d\n", arr[0]); // Prints 1 as it should
arr[0] = a;
printf(" Element 0 assigned to variable a: %d", a); /* Prints 67 ?? (Elements 1 and 2
are printed as 0 when assigned different variables.) */
I want my "a" variable to return same value as arr[0] (and then do it for two more variables for arr[1] and arr[2])
I have a text file with a few numbers called "Dane.txt", it's contents - "1, 2, -4,".
Here is the full code in which I try to solve a math problem with input taken from a .txt file (I got most of it from here):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
// Initializing the file pointer
FILE *fs;
char ch, buffer[32];
int i = 0, arr[100], j = 0;
int a, b, c, arr2[3];
// Openning the file with file handler as fs
fs = fopen("Dane.txt", "r"); // "Dane.txt" contents: 1, 2, -4,
// Read the file unless the file encounters an EOF
while(1){
// Reads the character where the seeker is currently
ch = fgetc(fs);
// If EOF is encountered then break out of the while loop
if(ch == EOF){
break;
}
// If the delimiter is encounterd(which can be
// anything according to your wish) then skip the character
// and store the last read array of characters in
// an integer array
else if(ch == ','){
// Converting the content of the buffer into
// an array position
arr[j] = atoi(buffer);
// Incrementing the array position
j++;
// Clearing the buffer, this function takes two
// arguments, one is a character pointer and
// the other one is the size of the character array
memset(buffer, 0, 32);
// clearing the counter which counts the number
// of character in each number used for reading
// into the buffer.
i = 0;
// then continue
continue;
}
else{
// reads the current character in the buffer
buffer[i] = ch;
// incrementing the counter so that the next
// character is read in the next position in
// the array of buffer
i++;
}
}
for(i = 0; i < j; i++){
arr2[i] = arr[i];
printf("Number [%d]: %d\n", i, arr[i]);
printf("%d\n", arr[i]);
}
printf("\n Element 0 when in array: %d\n", arr[0]); // Returns 1 as it should
arr[0] = a;
printf(" Element 0 assigned to variable a: %d", a); // Returns 67 ??
}
There is no place in your code where you assign any value to a, so when you print the value of a, you get whatever garbage happened to be in that area of memory.
arr[0] = a;
This is an assignment. The left-hand side is the variable assigned to, arr[0]. The right-hand side is the value assigned to it. If you are not clear on this, you may wish to study a good C reference's section on lvalues and rvalues.

How many characters is fscanf scanning in my code?

I'm using a for loop, which I want to iterate a number of times equal to the number of characters scanned by scanf. However, it seems to run too many times. The code was originally written to print to phrase "We are in 2019", which it does, but now I need to use it for something else. I added in the line: printf("%i",i);
to see how many times it was going through the for loop. It seems to be running 8 times for each run of the while loop, regardless of how long the word scanned was.
#include <stdio.h>
#include <stdlib.h>
int main() {
char* word = malloc(sizeof(char) * (46)); // create a char array
FILE* fp;
fp = fopen("file.txt", "w+");
fputs("We are in 2019", fp);
rewind(fp); // sets to start of file
while(fscanf(fp, "%s", word) != EOF) {
for(int i = 0; i < sizeof(word) / sizeof(word[0]); i++) {
printf("%c", word[i]);
printf("%i", i);
}
printf("\n");
}
fclose(fp);
return (0);
}
The output is:
W0e1234567
a0r1e234567
i0n1234567
200112934567
So I can see it's running the for loop 8 times for each run for each run of the while loop.
Am I misunderstanding how fscanf works? i thought it stopped at a whitespace, and only stored the preceding characters... e.g. first it would scan "We" then store it as a 3 character array in "words", then scan "are" in a 4 character array in "words" and so on. What is really happening?
It is stopping at whitespace.
Apparently you have 64 bit pointers. The variable word is a pointer, so sizeof(word) is 8. Since sizeof one character is 1, sizeof(word) / sizeof(word[0]) is 8.
So, the first time fscanf returns, it has read "We" into the buffer. Then it loops for i from 0 to 7.
i = 0
printf("%c", word[i]); //=> 'W'
printf("%i", i); //=>0
i = 1
printf("%c", word[i]); //=> 'e'
printf("%i", i); //=>1
i = 2
printf("%c", word[i]); //=> '\0' so nothing
printf("%i", i); //=>2
i = 3
printf("%i", i); //=>3
i = 4
printf("%i", i); //=>4
i = 5
printf("%i", i); //=>5
i = 6
printf("%i", i); //=>6
i = 7
printf("%i", i); //=>7
so that is how you get:
W0e1234567
You would see the desired output if your loop looked like this:
while(fscanf(fp, "%s", word) != EOF) {
puts(word);
}
sizeof(word) will not give you the size of the array. It gives the size of the pointer char *word.
It would be simpler to avoid memory allocation, and the size of the array isn't really relevant anyway.
#include <stdio.h>
#include <stdlib.h>
int main() {
char word[46]; // local array
FILE* fp = fopen("file.txt", "w+");
if(fp == NULL) {
return 1; // or better error report
}
fputs("We are in 2019", fp);
rewind(fp);
while(fscanf(fp, "%45s", word) == 1) { // restrict length, check the scan worked
for(int i = 0; word[i] != 0; i++) { // finish when the string terminator is found
printf("%c", word[i]);
}
printf("\n");
}
fclose(fp);
return 0;
}
Program output:
We
are
in
2019
Several remarks can be done on your code
by definition sizeof(char) is 1, so your malloc(sizeof(char) * (46)) can be simplified to be malloc(46). Note you can also use an array char word[46];
fscanf(fp, "%s", word) is very dangerous, if the read word longer than 45 characters you write out of word with an undefined behavior, limit the size doing fscanf(fp, "%45s", word)
As said in the remarks sizeof(word) / sizeof(word[0]) do not return the length of your word, you can just stop when word[i] is 0
to print the index just after each character do not produces a very readable result, are you sure you want that ?
why do you not directly print the word by (f)puts or printf with the format %s and get the length of the string using strlen (if you want to know it) ?
I want to iterate a number of times equal to the number of characters scanned by scanf.
scanf scans more than the number of characters in returns in the string, the spaces are bypassed silently. Only one thing is sure, when you reach the end of the file without error the number of characters scanned by scanf if the size of the file.

Trying to check if a number is a palindrome through the use of strings [duplicate]

This question already has answers here:
How do I check if a number is a palindrome?
(53 answers)
Closed 5 years ago.
I am trying to check if an input number is a palindrome. I am doing it through strings rather than ints. So, I am taking in a string and reversing it into another string. However, when I use the string compare function it does not give me 0, stating that the strings are not the same. Even when I put in for example "1001", both the input and reverse strings displays 1001. I have figured it out with other methods but am trying to understand what is wrong with this one in specific.
#include <stdio.h>
#include <string.h>
int main(void)
{
char input[100];
char reverse[100];
int numLen = 0;
printf("Enter a number\n");
fgets(input, 100, stdin);
printf("The number is: %s\n", input);
numLen = strlen(input) - 1;
printf("Length of string is: %d\n", numLen);
for (int i = 0; i < numLen; i++)
{
reverse[i] = input[numLen - 1 - i];
if (i == numLen - 1)
{
reverse[i + 1] = '\0';
}
}
printf("The reverse number is: %s\n", reverse);
printf("The original number is: %s\n", input);
int result = strcmp(input, reverse);
printf("Result of strcmp gives us: %d\n", result);
if (strcmp(input, reverse) == 0)
{
printf("These numbers are palindromes\n");
}
else
{
printf("These numbers are not palindromes\n");
}
return 0;
}
The problem is you are not handling the strings properly. You should overwrite the '\n' with \0.
...
char input[100];
char reverse[100];
int numLen = 0;
printf("Enter a number\n");
fgets(input, 100, stdin);
printf("The number is: %s\n", input);
input[strcspn(input,"\n")]='\0'; // getting the length of the
// string without `\n`
// and overwriting with `\0`
numLen = strlen(input) ; // now you don't need to put the -1
printf("Length of string is: %d\n", numLen);
for (int i = 0; i < numLen; i++)
{
....
Apart from these two changes everything else remains the same. You were reversing it all right. And then you used strcmp right way. But the extra \n is removed in the code I have shown.
(still) Why it works?
Now to give you a better idea. You formed the reversed string alright. But the original string has \n within itself.
printf("The reverse number is: (%s)\n", reverse);
printf("The original number is: (%s)\n", input);
In the previous program you just do write these two lines. You will understand where you went wrong.
On giving input 1001Enter it gives this output.
The reverse number is: (1001)
The original number is: (1001
)
What is strcspn doing?
I have using strcspn function got the length without \n and overwriting it with \0.
0 1 2 3 4 5 --> indices
1 0 0 1 \n \0 --> strcspn(input,"\n") returns 4.
1 0 0 1 \0 \0 --> input[strcspn(input,"\n")]='\0'
You can do simply like this without the copying and everything.
Without extra memory - in place palindrome checking
bool checkPal(const char *s){
for(int i = 0, j= strlen(s)-1; i< strlen(s) && j>=0 ; i++)
if(s[i] != s[j])
return false;
return true;
}
int main(void)
{
char input[100];
char reverse[100];
printf("Enter a number\n");
if( fgets(input, 100, stdin) )
printf("The number is: %s\n", input);
input[strcspn(input,"\n")]='\0';
int numLen = strlen(input) ;
printf("Length of string is: %d \n", numLen);
printf("These numbers are %spalindromes\n", checkPal(input)?"not ":"");
return 0;
}
A more succinct way to write the checkPal() would be,
bool checkPal(const char *first){
const char *last = first + strlen(first);
while (first < last) {
if (*first++ != *--last) {
return false;
}
}
return true;
}
last points to the \0 character. Subtraction is necessary before we start doing comparison. To get a clear idea of what happens you have to know the precedence and few rules.
The first<last part is obvious. We are comparing till we reach a point where we first > last (For even length strings) or first = last (for odd length strings).
The if is a bit tricky. *first++ there are two operators involved. * (indirection) and ++(post increment).
And precedence of ++ is higher than de-reference *.
So *first++ will be - first is incremented. Then you might think that we are missing one character very first time but that's not the case. Value of a postfix expression is the value before we do first++. So now you have the first character.
Same way *--last will have the same effect except the value of the prefix expression is the value after the operation. So you are considering the last character.
If they matches we continue. first and last already contain the modified value. We repeat the same logic for rest of the characters in the smaller sub-string.
If a mismatch occurs then we return immediately. (Because it's not a palindrome).
Sorry, my bad. Try this:
#include <stdio.h>
#include <string.h>
// A function to check if a string str is palindrome
void isPalindrome(char str[])
{
// Start from leftmost and rightmost corners of str
int l = 0;
int h = strlen(str) - 1;
// Keep comparing characters while they are same
while (h > l)
{
if (str[l++] != str[h--])
{
printf("%s is Not Palindromen", str);
return;
}
}
printf("%s is palindromen", str);
}
// Driver program to test above function
int main()
{
isPalindrome("abba");
isPalindrome("abbccbba");
isPalindrome("geeks");
return 0;
}
Does this one work?
A variant, recursive version that has no more that the string as argument (or a copy of the original string)
int pal(char *s) {
int n = strlen(s);
if (n <= 1) return 1;
if (s[0] != s[n-1]) return 0;
s[n-1] = '\0';
return pal(++s);
}
return 0: not a palindrome, 1: is a palindrome
Note the string is altered, so you can call it this way if it's a problem (or if the string is created in a static area)
char *copy = malloc(strlen(string)+1); // string is original string
strcpy(copy, string);
int ispal = pal( copy );
printf("Is %s a palindrome\n", ispal ? "":"not");

Resources