Defining variables at the beginning of function and "fprintf" - c

I'd like your help with couple of things I'm not sure of them, even-though the compiler doesn't complain about them:
Here I need to write a program which gets input and output, in the input file were stored integers which I don't know their amount divided by spaces, I need to read these numbers, sort them by the sum of digits comparison and print out the sorted numbers in the output file. this is what I wrote, after that a couple of short question about this code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
int myComp(const void *a, const void *b){
int x=(*(int*)a);
int y=(*(int*)b);
int sumForx=0;
int sumFory=0;
while (x){
sumForx=sumForx+(x%10);
x=(x-(x%10))/10;
}
while (y){
sumFory=sumFory+(y%10);
y=(y-(y%10))/10;
}
if (x>y) return 1;
else if (x<y) return -1;
else return 0;
}
int main(int argc, char** argv) {
FILE* inFile;
FILE* outFile;
int size=0;
int tmp;
if (argc!=3) {
printf("Please enter 3 arguments");
assert(0);
}
inFile=fopen(argv[1], "r");
if (inFile==NULL) {
printf("path to the input file has not found");
assert(0);
}
outFile=fopen(argv[2], "w");
if (outFile==NULL) {
printf("path to the output file has not found");
assert(0);
}
while (fscanf(inFile, "%d", &tmp)==1) {
size++;
}
int arr[size];
fseek(inFile, 0, SEEK_SET);
int i=0;
while (fscanf(inFile, "%d", &tmp)==1) {
arr[0]=tmp;
i++;
}
qsort(arr,size,sizeof(int),myComp);
int j;
for (j=0;j<size;j++){
fprintf(outFile,"%d",arr[j]);
fprintf(outFile,"%c",' ');
}
fclose(inFile);
fclose(outFile);
return 1;
}
I kept define new variables during the main, in different places- I can recall that I shouldn't do that since all variables must be defined at the beginning of the function, unless there are local variables of inner functions/brackets, and this is not the case here- but still the compiler is fine with this- what's the correct thing to do?
If the answer the (1) is "you must define all variables at the beginning of the function"- in my case, I must define dynamic int* arr and allocate space for it after I computed size, otherwise I can't use int arr[size] since size is computed after all the variables have defined already, include the integer array.
3.I want to enter a space between these numbers while being printed to the file, is fprintf(outFile,"%c",' '); correct after putting integer every time?
4.any other correction would be gladly welcome!

The requirement to declare all variables at the beginning of a function dates back the the pre-previous version of the standard (C89, which was outdated by C99, which was outdated by C11).
Since you are using a variable-length array (arr[size]), which wasn't possible in that pre-previous version of the standard, you are obviously using a halfway-decent compiler that doesn't stick to restrictions no longer applying since 1999. ;-)
As for printing a space, fprintf( outfile, " " ) or (even better) fputc( ' ', outfile ) would do.
As for further corrections, I have a habit of not reading / commenting on uncommented source. You have a coding style with which I violently disagree, but at least you're consistent in applying it. ;-)

This depends on the version of C you are using. If you use ANSI C (c89) you will get warnings or errors about this. But most (if not all) modern compilers support declarations almost anywhere in the code.
This again depends on your compiler. Old compilers couldn't allocate static arrays like that, modern compilers can.
fprintf(outFile, " "); is perfectly legal too.

If you are using gcc, it will allow you to place variables anywhere in the function. However, other compilers (e.g. MSVC 2008) follow ansi c i.e c89 and probably will complain about it. The correct thing to do depends on your application. If you want it to be portable across compilers, declarations should come before statements.
This is also compiler and C standards specific, so reasoning similar to point 1 applies.
For this, your code seems fine but I use something like:
fprintf(outFile, "%d ", arr[j]);
to achieve similar functionality in a single line.

printf("Please enter 3 arguments");
The executable name is usually not referred to as an argument so this would normally be
printf("Please enter 2 arguments - input filename and output filename e.g.:\n");
printf("%s file1.txt file2.txt\n",argv[0]);
This
arr[0]=tmp;
should be
arr[i]=tmp;

1: Declaring variables anywhere in the function body is fine as long as you are on a compiler that supports the C99 standard or later. Earlier compilers required that you declared variables at the beginning of a block scope, as you say.
You can do even better than in your code however, by limiting the scope of some of the variables even more. Instead of:
int j;
for (j = 0; j < size; j++) { ... }
You can simplify this to:
for (int j = 0; j < size; j++) { ... }
Which also limits the scope where j is accessible to the block following the for statement.
2: Dynamically sized arrays are only supported in C99 and later, so the code would not compile on a compiler that does not support the newer standard.
3: You can simply add the extra space to the initial format string:
fprintf(outfile, "%d ", arr[j]);

Related

whole array printing instead of a single value within it

I need to be able to read in a word from a file one at a time and then be able to sort the text into a struct to track how many times words have been repeated however whenever I try to point to a specific word in the file I'm getting the whole file value instead of the specific word I'm trying to retrieve. apologies if the solution is simple, I still struggle with the different types of pointers and file commands
#include <stdlib.h>
#include <stdio.h>
char *inputFilename;
char *outputFilename;
char inputText[5000];
char outputText[5000];
int inputFlag;
int outputFlag;
int readfile(char **data){
FILE *input;
input = fopen(inputFilename, "rb");
int len = sizeof(*input);
printf("input length is %d\n", sizeof(char));
//First value is number of integers
int size;
fread(&size, sizeof(char), 0, input);
//allocate memory for that number of values
*data = (char*)malloc(sizeof(char) * size);
//Read in rest of data
fread(*data, sizeof(char), size, input);
//return size
printf("size is %d\n", size);
return size;
}
int valueSearch(char *vData, int argCount, char **argv){
for(int argLoop = 0; argLoop < argCount; argLoop++){
//If vData is detected then the next argument is the input file name
if(strcmp(argv[argLoop], vData) == 0){
return argLoop + 1;
}
}
return 0;
}
int main(int argc, char **argv){
char *data;
inputFlag = valueSearch("-i", argc, argv);
printf("input flag is %d\n", inputFlag);
inputFilename = argv[inputFlag];
if(inputFlag == 0){
printf("Please enter the text you would like to sort: ");
fgets(inputText, 5000, stdin);
}
else{
int size = readfile(&data);
}
int i = 0;
//Value that should be placed into struct
printf("readfile value 0:\n%s\n", data[i]);
free(data);
return 0;
}
Your last printf uses %s instead of %c. %s treats the argument as a pointer to a string (char array), and prints each byte until it encounters a byte of 0. You want %c, which prints out a single char.
Use %c instead of %s because %s is a identifier of string and that will print whole string.
printf("readfile value 0:\n%c\n", data[i]);
Hopefully it will work for you.
You need to become familiar with compiling with compiler warnings enabled, and then do not accept code until it compiles without warning or error.
You invoke Undefined Behavior on line 19 and again on line 76 by using improper conversion specifiers for int (instead of long int - line 19) and again as already noted on line 76 where you use %s instead of %c.
Further, you do not include string.h which is required for the use of strcmp on line 41. There is no declaration for strcmp so your compiler makes a guess at an implicit declaration.
You will also find unused variables len and size in your code. (lines 17 and 69, respectively).
All of these problems are readily apparent from your compiler output if you compile with -Wall -Wextra (and if wanted -pedantic) on gcc, with -Weverything on clang, or /Wall with VS (cl.exe) (you may want /W3 on VS as /Wall means all (and you will have to individually disable a handful of warning there with /wdXXXX where XXXX is the code for the warning you wish to disable).
While not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.
Moral of the story -- (1) enable compiler warnings, and (2) do not accept code until it compiles cleanly -- without warning. Letting your compiler help you write better code will eliminate a large percentage of the errors you spend time chasing :)

Get the multiples from a file and copy them on another

My intention is to get all the numbers that exist on the file multiples.txt and write the multiples of the desired integer (input by the user).
#include <stdio.h>
int main() {
FILE *f, *fs;
int value, multiple, n;
f = fopen("multiples.txt", "r");
if (f == NULL)
printf("Error\n");
fs = fopen("exit.txt", "w");
if (fs == NULL)
printf("Error\n");
printf("Write a number\n");
scanf("%d", &value);
do {
n = fscanf(f, "%d", multiple);
if (multiple % value == 0) {
fprintf(fs, "%d", multiple);
}
} while (n != EOF);
fclose(f);
fclose(fs);
}
My program crashes and I can't figure where it comes from.
I'm going to assume the original c++ tag was actually correct, and give an answer that (I think) makes better use of the capabilities of C++ instead of just C.
Since we want to copy items that meet a particular criterion, we can use the std::copy_if algorithm to handle the majority of the work. We also need to specify the "rule" for what items to copy. We typically want to use streams instead of C-style FILE *s, especially since the latter don't support iterators.
Taking those ideas into account, we'd write the code more like this:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <iterator>
int main() {
std::ifstream in("multiples.txt");
std::ofstream out("exit.txt");
std::cout << "Enter a number: ";
int n;
std::cin >> n;
std::istream_iterator<int> begin(in), end;
std::copy_if(begin, end,
std::ostream_iterator<int>(out),
[n](int i) { return i % n == 0; });
}
As you can see, this eliminates the opportunity for the error that originally prompted the question (along with the error you had in dealing with the end of file incorrectly).
If you are going to do the job in C, you usually want to combine reading input with testing whether the input succeeded to get a loop that reads to the end of a file, then stops at the right time. In this case you're reading with fscanf, so one fairly easy way to write the loop correctly would look something like:
while (1 == fscanf(f, "%d", &multiple))
if (multiple % value == 0)
fprintf(fs, "%d", multiple);
The return value from fscanf is the number of "items" successfully converted. Here we're asking for one integer to be read/converted, and then testing whether that happened.
One minor addition (that applies to both): I've written the code above to match that in the question in one respect: it writes out the resulting numbers without any delimiter between them. For example, if you had an input containing 2 4 5 8 and the user entered 2, it would produce output of 248, so in the result you wouldn't be able to tell which digits came from which inputs. In real use, you almost certainly want to separate them with something (commas, spaces, new-lines, etc.)
If you just enable warnings (-Wall for starters), the compiler just tells you:
test.cpp:20:29: warning: format specifies type 'int *' but the argument has type 'int' [-Wformat]
n = fscanf(f, "%d", multiple);
~~ ^~~~~~~~
You forgot to take the address of multiple
Always use the tools to spot your errors
There are many other issues that can be solved with a trip to the documentation (e.g. http://linux.die.net/man/3/scanf)
n=fscanf(f, "%d", multiple);
I thinks this point has a little problem
n=fscanf(f, "%d", &multiple);
add "&"

Inserting 400 numbers in a text file

I am trying to write numbers from 1, up to 400 in a text file. I am using the code below, which is running without any errors, but the file is being left empty.
Any help would be appreciated.
#include <stdio.h>
int main(void)
{
FILE *filePointer;
filePointer = fopen("file.txt","w");
int i;
for(i=0; i > 400; i++)
{
fputs("%d, ",i,filePointer);
}
fclose(filePointer);
return(0);
}
No, there's no way that compiled without some serious-sounding warnings at least.
You're using fputs() as if it were fprintf(), passing it an integer instead of a FILE pointer (which the compiler should not allow) and an extra argument (which the compiler should not allow).
Also your for loop is broken. The middle part is an expression that should be true for as long as the loop should run, not the other way around.
You meant:
for(i = 0; i < 400; ++i)
{
fprintf(filePointer, "%d, ", i);
}
Also, you should check that the file really did open before assuming it did. I/O can fail.
Apart from the fputs() usage, the problem is:
for(i=0; i > 400; i++)
If you initialize a variable with zero and perform a loop as long as it's greater than 400, that won't last too long.
The fputs syntax seem wrong. I think it is:
int fputs(const char *str, FILE *stream)
Pick #unwind's approach (as mentioned above) but if you still want to use fputs then your fputs line should be expanded into 3 lines:
char temp[4]; // String to store 3-digit number + '\0'
sprintf(temp, "%d, ", i); // Prepare a string for a given number
fputs(temp, filePointer); // Write the string to the file
This should work. #happycoding :)
PS: You seem to be following a bit C++ standard in declaring a variable anywhere. It is not pure C. #justsaying

Beginner help: Parsing in C

I'm relatively new to this concept for parsing. And here is a simple, yet for me, it's mindbreaking, example.
I have a text file containing a series of numbers and letters. In each line of the text there are three elements. a letter, another letter, and a number. Consider the first as the source, the second as the destination and the number as size. The read them and put them into a structure array and be able to arrange them according to size. "a, b, 1" for the first line. "q, s, 5" for the 2nd, etc. And lastly, printing them in an arranged format (which is according to size)
Mind giving me a clues or starting points?
Update:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
int main(){
FILE *fp;
fp= fopen("file.txt", "O");
int i;
struct arrangement{
char source;
char dest;
int cost;
};
struct arrangement rng[22];
for(i=0; i<22 ; i++){
fscanf(fp, "%c, %c, %d", rng[i].source, rng[i].dest, rng[i].cost);
printf("%c, %c, %d", rng[i].source, rng[i].dest, rng[i].cost);
}
getch();
return 0;
}
will this be able to "store all elements in the array?I still don't have any idea how I will arrange these according to size/cost without the source and destination being left out.
fscanf requires pointers to the variables, not the variables themselves. Your code may result in strange results, depending on compiler (gcc may emit a warning/error) and platform.
You also should break the loop if reaching EOF. i may then provide the last used entry (which may be partially valid or not, depending on the input).

C: pointers mixup

#include<stdio.h>
#include<stdlib.h>
typedef struct{
char cStdName[50];
int nStdNum;
char cStdClass[4];
float dStdAvg;
}student;
student* students;
int cmp(const void* a, const void* b);
void main() {
int num = 0,i=0;
FILE *f;
printf("Number of students:");
scanf("%d", &num);
students = (student*)malloc(num*sizeof(student));
for(i=0;i<num;++i){
student* ptr = students+i*sizeof(student);
printf("Name:");
scanf("%s", ptr->cStdName);
printf("Num");
scanf("%d", &ptr->nStdNum);
printf("Class:");
scanf("%s", ptr->cStdClass);
printf("Grade:");
scanf("%f", &ptr->dStdAvg);
}
f = fopen("bin.bin","wb");
fwrite(&num,sizeof(int),1,f);
fwrite(students,sizeof(student),num,f);
fclose(f);
system("pause");
}
This is supposed to output the number of students and all the structure 'array' in a binary file and it works with 1 student. But when I add >=2 people, the file looks like this:
http://i.imgur.com/LgL8fUa.png
If I add only 1 student, there is still some of this Windows path nonsense:
http://i.imgur.com/s7fm9Uv.png
It is OK though, the program that reads the file ignores everything after the NULL(I mean, for the first char array).
I think the problem is somewhere in the for() loop and the pointer juggling but I can't tell where.
student* ptr = students + i * sizeof(student);
In C, pointer arithmetic already includes sizeof(student). You will read past the end of your arrray.
student* ptr = students + i;
However, you'll notice accessing to ptr is the same as accessing to students[i].
There are a few issues with your code:
First of all, as Kirilenko said, you should use students[i] in your code. In your case, students + i * sizeof(student) goes out of bounds.
It's never a good idea to use fwrite with an array of structs. That's because the compiler may add some space between the members of a struct (padding), which means that when you pass an array of structs to fwrite, the padding bytes (which will contain garbage) will be printed.
The same also applies to the char array members in your struct. All unused bytes will contain garbage, which will be printed when you use fwrite. It's better to use strlen to determine how many read characters each char array contains.
Here's how I'd write the students' array to a file:
void write(students* array, int len, FILE* out)
{
int i;
fwrite(len, 1, sizeof(len), out);
for (i = 0; i < len; i++){
fwrite(array[i]->cStdName, 1, strlen(array[i]->cStdName), out);
fwrite(array[i]->nStdNum, 1, sizeof(array[i]->nStdNum), out);
fwrite(array[i]->cStdClass, 1, strlen(array[i]->cStdClass), out);
fwrite(array[i]->dStdAvg, 1, sizeof(array[i]->dStdAvg), out);
}
}
Kirilenko's answer should solve your immediate problem, but there are errors ranging from trivial to severe on nearly every line of this program. Depending on what your larger goal is, you might not need to fix all of them, but I'm going to write them all down anyway, to illustrate the size of the iceberg.
void main() {
int main(void). The int is an absolute requirement. In C (not C++) writing () for a function argument list means the arguments are unspecified, not that there are no arguments; technically you can get away with it here because this is a function definition, but it's bad style.
The opening curly brace of a function definition always goes on a line by itself, even if all other opening braces are cuddled.
int num = 0,i=0;
Inconsistent spacing. Initializations are unnecessary.
printf("Number of students:");
Some style guides prefer fputs("Number of students", stdout); when you're not using printf's formatting capabilities. But some compilers can do the transformation for you, and it's not a big deal.
scanf("%d", &num);
Never use scanf, fscanf, or sscanf, because:
Numeric overflow triggers undefined behavior. The C runtime is allowed to crash your program just because someone typed too many digits.
Some format specifiers (notably %s, which you use later in this program!) are unsafe in exactly the same way gets is unsafe, i.e. they will cheerfully write past the end of the provided buffer and crash your program (this particular program doesn't look security-sensitive to me, but one should always code as if one's programs are at least somewhat dangerous that way).
They make it extremely difficult to handle malformed input correctly.
The correct way to read a single nonnegative number from the user is like this:
unsigned long getul(void)
{
char buf[80], *endp;
unsigned long val;
for (;;) {
fgets(buf, 80, stdin);
val = strtoul(buf, &endp, 10);
if (endp != buf && *endp == '\n')
return val;
if (buf[strlen(buf)] != '\n')
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter one nonnegative integer, smaller than %lu.\n",
ULONG_MAX);
}
}
You can get away with a fixed-size buffer here because nobody's ULONG_MAX is so large that it doesn't fit in 80 characters.
students = (student*)malloc(num*sizeof(student));
You should use calloc here, so that when you go to write your structures to disk, they aren't full of junk. (Or write custom serializers as suggested by Alexandros, that will also avoid the problem.)
for(i=0;i<num;++i){
Preferred style is for (i = 0; i < num; i++) {. In addition to the spacing, use i++ instead of ++i so that i appears in the same position in all three expressions; this makes it easier to read.
student* ptr = students+i*sizeof(student);
student* ptr = students + i; as discussed elsewhere.
printf("Name:");
scanf("%s", ptr->cStdName);
See comments above. You want another helper function, like so:
void getstr(char *buf, size_t len)
{
size_t n;
for (;;) {
fgets(buf, len, stdin);
n = strlen(buf);
if (n < len && buf[n] == '\n') {
memset(buf+n, 0, len-n);
return;
}
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter no more than %lu characters.",
(unsigned long)(len-1));
}
}
...
scanf("%f", &ptr->dStdAvg);
And here you need another helper function. getf is exactly the same as getul except it uses strtod, and of course the error message is a little different.
f = fopen("bin.bin","wb");
Isn't that an awfully generic file name? There should probably be a way for the user to specify it.
fwrite(&num,sizeof(int),1,f);
Your file format needs a magic number.
fwrite(students,sizeof(student),num,f);
You are writing binary data to disk in CPU-endian order. That may not be a problem for this application, but be aware that you might have a cross-platform compatibility headache down the road. (Personally, for what it looks like you're doing, I would use a textual serialization such as JSON, or a simple no-daemon database such as sqlite.) See Alexandros' answer for more potential problems with this file format.
It's rarely a problem when writing to files on disk, and this isn't the sort of program whose output gets piped somewhere, but still, I'm'a mention that fwrite does not guarantee to write all the data you provide it. Technically you have to call fwrite in a loop like this:
size_t r, n = sizeof(student) * num;
char *p = (char *)students;
while (n > 0) {
r = fwrite(p, 1, n, f);
if (r == 0) break; /* write error */
n -= r;
p += r;
}
For this to work correctly you must do the multiplication yourself and pass 1 for the second argument to fwrite; otherwise a short write may end in the middle of an "element of data" and you have no way of knowing that this has happened.
fclose(f);
Check for write errors before closing the file.
if (ferror(f) || fclose(f)) {
perror("bin.bin");
return 1; /* unsuccessful exit */
}
...
system("pause");
Just return 0. Programs that make you hit a key to exit are batch-unfriendly.
The pointer assignment can be outside for loop once and you can increment the pointer at the end of for loop. Try this.
Ok. Here is an attempt to explain what is happening, the ptr is pointing to first element in students list of struct types and when you increment the ptr at the end of the for loop, it points to the next student in the list.
---
students = (student*)malloc(num*sizeof(student));
student* ptr = students;
for(i=0;i<num;++i){
printf("Name:");
----
----
ptr++;
}

Resources